3d41758be0e11a8a523fe4ac297eadc6a4b16d36
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b793638484 |
v0.262.0: six defects two drill nights found in the update, remove and hold paths
gates / gates (push) Successful in 26s
R-630 (P1): waitUpdateHealthy kept the probe inside `if hc != nil && len(hc.Checks) > 0`, and when findProbeContainer returned "" its else set last="no probe container" and LOOPED - the settle path sat in the outer else, unreachable. So verifying could only time out and failAndHold then stopped a working app. Measured on paperless-ngx: three containers healthy, failed at +313.0s, front door 404 after. It now falls through to the same settle path with a WARN naming the candidates. The probe target is decidable now: HealthCheckConfig.Container plus findProbeContainerMeta resolve by exact stack name -> explicit container -> a UNIQUE prefix -> nothing with the candidates returned. The old rule took the FIRST prefix match. A skipped stack records why instead of silence. R-634 (half): RemoveStack refused on the !Deployed FLAG while the machine had containers, a compose file and an app.yaml. It now asks whether anything EXISTS. The mechanism producing the bad record is still not diagnosed and R-634 stays open for it. R-633/R-626: RemoveStack consults UpdateGuards.Busy and IsUpdating and refuses with the app's own sentence - the product already refused this clash for update and for restore. And because `down` returning 0 is a request not a result, the project is watched for 25s afterwards, anything carrying its label is removed by name with its labels logged, and the answer carries `verified`. R-621: failAndHold writes compose logs --tail 400 into <stackdir>/hold-logs/<ts>/ BEFORE the down that destroys them. Two existing tests pin the compose sequence and correctly caught the new step; their expectations are updated with the reason that the ORDER is the assertion. R-614: RemoveStack calls ClearUpdateState. NOT in this release: R-625 (a held app still renders an Update button). Named, not half-done. Three new sentences, each born as a key in both bundles. Four red-proofs seen failing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |
||
|
|
811f75736e |
v0.261.0 — the controller no longer swaps itself out from under an app update (R-608, R-609)
gates / gates (push) Successful in 23s
The controller self-updates daily at 04:30 by default, and after any hub report once a floor sits above the box. That swap restarts the controller container. The window proposed for automatic app updates is 02:30-05:00. It contains 04:30. R-608 — a two-way lock, wired in main.go (stacks never imports selfupdate): - stacks.Manager.AnyUpdating() -> Updater.SetAppUpdatingCheck, consulted in the same three places as the existing backupRunning gate. - Updater.IsUpdateRunning -> Manager.SetSelfUpdatingCheck; UpdatePreflight refuses `self_updating`. - MEASURED: the gap was narrower than assumed. The update's `backing-up` phase already takes the backup single-flight, so that one phase was covered. The other six were not, and `starting`/`verifying` are where data may have moved. - The lock must NOT latch: a held app does not block the controller's own updates, including the release that might fix the hold. R-609 — the 409 carries `data.reason`, additively. transient (busy, updating, deploying, migrating, self_updating) vs terminal (held, downgrade). Found while writing the test: the router refuses a HELD app on its own line before the preflight, so `held` would have been the one reason missing. Five red-proofs, each seen to fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |
||
|
|
8f8a64cad7 |
v0.260.0 — a box ahead of the catalog reads „Naprakész", and the pin never moves backwards (R-524)
gates / gates (push) Successful in 24s
MEASURED 2026-09-15 (BIGNIGHT Phase 6): privatebin updated 2.0.5 -> 2.0.6, catalog reverted to 2.0.5, and the box read „Frissítés elérhető — ma" over an Update that would have moved the pin BACKWARDS onto a possibly-migrated datadir. - stacks.CatalogOrder: the comparison gains a fourth answer (Ahead) and moves out of web, so the badge and UpdatePreflight cannot drift apart. - The badge: ahead reads „Naprakész"/"Up to date", tag-ok, with a title saying why. - The refusal: UpdatePreflight returns `downgrade` (409), born as a bundle key; the API now renders update refusals through errText so it reaches English households. - Ahead is narrow: every differing service must be orderable AND newer, else Behind. - Ordering is util.Version.Compare behind a tag normaliser — no second comparator. - Three red-proofs, each seen to fail. R-589 was already fixed in v0.258.0; only its register row was stale. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |
||
|
|
0fe04bb6d5 |
v0.259.0 — the claim page and the backup warnings answer in the reader's language (R-596, R-598)
gates / gates (push) Successful in 28s
The 2026-09-20 English drill ended one screen short: the claim page was English and its answers were Hungarian, so a household who mistyped the code from their e-mail could not tell a typo from a dead code. Fourteen call sites carrying nine messages now go through s.msg; the backup page's two protection warnings — which are promises about whether the customer's files are safe — follow the same route. Hungarian is byte-identical, proved structurally by the go-parity gate against the frozen base capture and red-proofed on a single added full stop. data["Title"] was DEAD (claim.html is standalone; .Title is layout.html's) and is deleted rather than translated — a translated dead field is a permanent false signal about where the page's title comes from. Six existing copy-contract tests were kept, not weakened: each now resolves its key through the real bundle, so it still convicts on a reworded Hungarian sentence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |
||
|
|
7c4a33b6d1 |
v0.258.0: the last four Hungarian things an English household met
gates / gates (push) Successful in 24s
R-589 the update badge, R-590 the data-folder backup sentence, R-573 the two channel banners, R-572 two dead helpers. All four were built in Go, which is why neither the template parity fixtures nor TestI18nEnglishPages could see them; slice 5's LIVE proof is what found them. R-590 is the one that matters most: it is a promise about the customer's files. It said, in Hungarian and under an already-English folder label, that a drop-zone is temporary and unbacked. The test now asserts the CONSEQUENCE in both languages — and that the two languages do not produce the same string, which would mean the English fell back. R-572 is not what its row said. The row claimed a template renders "vasárnap" on an English page; measured, NO template and no Go file called pruneLabel or nextPruneLabel. They were dead func-map entries returning Hungarian, so they are deleted rather than translated — translating dead code would add machinery with no reader and a test pinning a fiction. Deletion is fail-loud and that was proven: a template naming the removed function panics loadTemplates at startup. Five red-proofs. One of them says something about the gate rather than the code: the Go-parity gate does not measure localeFuncs keys against the base capture, so the citation to TestLocaleFuncsHungarianBundleMatchesFuncMap is what carries them — the test was extended to make that citation true. MinAgent: 0.131.0 (unchanged). Hungarian byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |
||
|
|
bef39598d0 |
v0.256.0: the box sends its own sentence in the household's language (R-558 Part B)
gates / gates (push) Successful in 24s
MinAgent: 0.131.0 (unchanged). Needs hub v0.118.0+, which shipped first and tolerates a box that sends none of this - every box in the fleet is that box until this release reaches it. The hub writes a household's e-mails in their language now, but about a third of those mails carry a sentence the BOX composed, naming a drive, an app or a number. The hub cannot translate one. So the box sends it twice. - message_customer on POST /api/v1/event, omitempty. A HUNGARIAN household sends nothing extra at all, so its payload stays byte-for-byte what every box sends today and the hub's fallback path keeps being the one production exercises rather than a branch nobody takes. - 19 producers render both sentences from ONE bundle key. `message` stays Hungarian always: it is what the operator is mailed and what the hub logs. - customer.language bootstraps a new box - stored choice, then config, then Hungarian. The config value is NEVER written into settings.json: that would record a choice the household never made. The Hungarian did not move, measured twice: the wire golden from the slice-2 base commit, and the Go parity gate over all 19 new keys. Three guards had to learn the change and one caught me: the test seam now carries the new field; the R-329 severity register reported two dynamic sites as no longer existing the moment they moved off PushEvent (the walk now checks 36 severity literals, up from 20); and TestConfigLanguageIsWiredInMain reads main.go, because cmd/ is gitignored and ripgrep does not. A mistake, named: the first pass dropped displayName from three producers, which would have mailed customers "Alkalmazás telepítve: %!s(MISSING)". Caught reading the diff; now pinned by a test that refuses %!/MISSING/%s/%d in either language. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |
||
|
|
48f3336956 |
v0.254.0 — the saved notes follow the language, and the switch becomes a globe (R-557 slice 2 release C; SLICE 2 CLOSED)
gates / gates (push) Successful in 23s
The notes a background run SAVES — last night's backup line, the last error, the proof result, the restore outcome — are written in the BOX's language at the moment they are written. A household that switches sees the previous run's note in the old language until the next run rewrites it: the operator's §16 option 1, stated rather than hidden. EndRestoreOp no longer receives a Hungarian literal from anywhere. The language switch is a globe. Two text links wrapped in the sidebar footer and asked the reader to recognise "Magyar"/"English" as links; a globe is the one symbol every web user already reads as "language", so nobody has to read Hungarian to escape Hungarian. It is <details>/<summary> — a menu with no script, drawn inline because the icon sprite lives only in layout.html and the visitor pages have their own shell. Those visitor pages get the same globe, and a visitor's choice stays theirs: a display-only felhom_lang cookie that langFor reads ONLY when there is no session. A signed-in household can never inherit a language a previous visitor picked in the same browser. POST /lang is CSRF-exempt for a narrow reason written at the exemption — its only achievable effect is the language of the page the victim's own browser shows them — and safeBackPath refuses //evil.example as well as https://, because "starts with /" alone is not the test. §16 taken: a successful claim carries the cookie into the household's setting. TWO PARITY EXCEPTIONS, MEASURED: 106 fixtures compared with a real diff — exactly two change shapes (the dashboard footer, the globe in the shells) and 5 byte-identical, which are the three pages that must not change. I INTRODUCED A DEADLOCK AND THE SUITE CAUGHT IT BY HANGING. UpdateOffboxStatus holds the settings write lock while running its callback; boxLang() wants the read lock; sync.RWMutex is not reentrant. On a real box an off-site run would have hung forever HOLDING the settings lock. Fixed by resolving the language before the callback, and guarded by a test that names the file and line in a second instead of hanging for 25 minutes. MinAgent: 0.131.0 (unchanged). No hub release needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |
||
|
|
7c05b59708 |
v0.253.0 — errors carry the key of the sentence they are (R-557 slice 2 release B)
gates / gates (push) Successful in 24s
179 Hungarian sentences were built deep inside a package with fmt.Errorf and printed by whoever caught them: too late to translate where they are shown, too early where they are made. Every one now carries its key across that gap. ZERO Hungarian error literals remain. util.MsgError does three things at once, each earned: - Error() is the Hungarian, byte for byte, so every un-converted printer is unchanged; - errors.Is answers for the kind AND for a wrapped cause (KindErrorf dropped the cause); - an error ARGUMENT renders recursively, so "formázás sikertelen: %w" translates whole. A foreign error — restic, docker, ssh, the stdlib — prints verbatim. It is not ours. 76 display sites go through errText, and TestNoErrErrorInPageOutput convicts any that do not. memoryVerdict returns an error rather than a sentence, so the deploy's 409 and the household's language come from one value; UpdateRefusal gained a Cause to carry it. Plurals, one rule, stated once: a key with .one/.other takes its COUNT first. Not a per-call-site flag — the producer somebody forgot would read "3 app is not running". The guard caught a real key collision (alert.deadapp.one) the day the rule landed. TWO DEFECTS FOUND IN MY OWN TOOLING, recorded rather than quietly fixed. The bulk converter silently dropped multi-line concatenations, damaging 7 producers — and the parity gate could not see it, because every surviving fragment WAS a real base literal while the CALL had lost text; two behaviour tests caught it. And the counting script was case-sensitive, so it said "0 left" while five remained. MinAgent: 0.131.0 (unchanged). No hub release needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |
||
|
|
5270bad76e |
v0.252.0 — the sentences the program builds follow the language (R-557 slice 2 release A)
gates / gates (push) Successful in 23s
Slice 1 translated the dashboard's markup. The sentences the program BUILDS were still Hungarian literals in Go, so an English household clicked an English button and was answered in Hungarian. 226 of them move into the bundle here. A flash was the hard part: it travels inside the redirect URL and is rendered by a DIFFERENT request, so it now carries a bundle key plus its parameters. A link minted by an older controller carries prose and is shown verbatim — never a raw key, never dropped. Also converted: page data and view-model text, the internal/api JSON answers, the alert banners (Alert.MessageKey, rendered on the way out of GetAlerts), 237 country names at display, and the four page titles built around an app name (R-566 closed). Hungarian is byte-identical, and that is measured rather than read: scripts/i18n_go_parity.py freezes every Go literal at the base commit (7 467) and refuses a key whose Hungarian is not that text, byte for byte. Three decoys, each seen to convict. Its own first version filtered the capture through an ASCII-Hungarian word list and missed seven real literals — the R-565 class. The filter is gone. Nothing on the wire moved, and wire goldens now hold it there: the report's health warnings and every notify event message stay Hungarian, because the hub MAILS the controller's sentence when it has no entry of its own. Slice 3 (R-558) owns those. MinAgent: 0.131.0 (unchanged). No hub release needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS |