PKG-16: cut 0.10.1, so SAFETY-57's fix can reach an installed 0.10.0 #105

Merged
admin merged 1 commits from PKG-16-release-0.10.1 into release/1.0 2026-09-22 15:52:26 +02:00
Owner

What this is

A version cut, nothing else. config.yaml and CHANGELOG.md, and no third file.

master and release/1.0 both say 0.10.0 today. The reference box is
running 0.10.0. The Supervisor decides that an update exists by comparing
version strings, so SAFETY-57 — merged at b9f7bd0 — currently has no
way of reaching the box that has the defect
. The bump is the delivery
mechanism, not bookkeeping. That is the whole of this ticket.

SAFETY-57 fixes a deadlock that parked the reference battery for about
thirteen minutes on 2026-09-21 while the house exported 2.4 kW, and which
ended only because a human restarted the add-on.

1. The cut

config.yaml: 0.10.0 → 0.10.1. A patch, because SAFETY-57 is a bug
fix to a version that has shipped.

⚠️ The option surface does not move at all — no key added, none removed, no
default changed, no enum widened, no rule added to validate.py. So a stored
blob that starts the add-on on 0.10.0 starts it on 0.10.1, and there is no
SystemExit(78) on this hop. That is what makes it a patch rather than a
minor, on the same test ## 0.10.0 applies to itself.

CHANGELOG.md opens a ## 0.10.1 section holding SAFETY-57's entry, moved out
of ## 0.10.0.

Two constraints in test_version_gate.py that the brief did not name, and
that the new section had to satisfy:

  • part 11a's second control requires the newest section to carry exactly
    one
    in merge order since contents table. The new section has one, based at
    cb3f686 (PKG-14's 0.10.0 cut, read off git log -L on the version: line
    rather than assumed).
  • part 11c requires every id in app/price.py's RETIRED_PROVIDERS to be
    named in the newest section. Those three ids were named in ## 0.10.0,
    which is no longer newest. They are now named in ## 0.10.1 too — which is
    honest rather than a gate-appeasement, because ha addons update installs
    0.10.1 in one step and a box older than 0.10.0 still meets that refusal on
    this hop. The section says so and points at ## 0.10.0 for the detail.

11b (version ↔ newest heading) and 11d (exactly one ## Unreleased below the
newest section — PKG-08's 0.8.0-era block, untouched) both hold.

2. What an operator needs in order to decide

SAFETY-57's entry opened by explaining that it was misfiled under 0.10.0 and
that whoever cut the next version owned moving it. This is that cut, so
that paragraph is spent. What replaces it is the decision:

  • what it does — the command is parked at 0 W, the battery-power
    reading is gated to MISSING, and the only condition that would clear the
    gate is one a parked command cannot produce. A deadlock, not a slow recovery.
  • how you recognise it — the setpoint sits at exactly 0 W and does not
    move while the house is plainly importing or exporting, the battery-power
    sensor is flat at one number throughout (−3 W for 850 s on the live
    episode), and the action log carries a STALE batt line with no clearing
    line after it.
  • what ends it — a restart of the add-on, and a restart is enough:
    nothing is latched to disk and the pack is not harmed. But nothing ends it by
    itself. On 0.10.1 the state releases itself after a bounded 32 s.
  • what it is not — not an emergency. 0 W is the safe command. What is lost
    is the site's own control while it lasts.

3. PKG-16 proper — the false preamble

## 0.10.0's preamble still claimed 0.10.0 "has never been built or pulled
anywhere"
and that the reference install was on 0.7.0. Both stopped being
true on 2026-09-20 ~22:18 CEST, when 0.10.0 was installed as a repository
add-on (slug 68285ac7_goodwe_controller) and began regulating a real battery.
This text is what the Supervisor shows beside the Update button.

Corrected in four places — the "upgrading from 0.7.0, which the reference
install is" clause, the predeploy_check transcript's framing, the no-op
check's "the box reports 0.7.0", and the closing bump argument. The old
argument is retold as history rather than deleted, because it was stated as
conditional and the useful record is that the condition fired. The section now
says what the field history bought: a full day-night cycle including an empty
pack, a guard trip, a 2 h 47 min saturation freeze, the SAFETY-57 deadlock and
a clean recovery — and two defects that no test in this repository reached,
SAFETY-56 and SAFETY-57.

4. The three PR #104 documentation defects

Fixed only where they live in CHANGELOG.md, per the brief.

# defect in CHANGELOG also in (left alone)
1446 the episode is 790 s, three places say 710 s ✅ fixed app/freshness.py:216, test_freshness.py:924-938
1447 keep-alive pulse stated as measurement ✅ fixed app/freshness.py:210, test_freshness.py check name
1445 fixture-bounded "one window" claim ✅ fixed app/freshness.py, DOCS.md, part 10c check name

⚠️ 1446 was re-derived, not rescaled. Moving the threshold from 710 s to
790 s moves the count with it: reviewer comment 1446 reports >= 710 s: 17 of 93 and >= 790 s: 16 of 93 off the committed capture. The CHANGELOG now says
16 of 93 at or beyond 790 s. Writing "17 of 93 at 790 s" would have swapped
one wrong number for two.

⚠️ The other occurrences are deliberately untouched. They are in source,
tests and DOCS.md; touching them would make this a third, fourth and fifth
file, and none of them is worth delaying a live safety fix. test_freshness.py
still asserts at the 710.0 threshold, so the suite and this file now disagree
about which number names the episode
— that is a known, stated gap, and it
wants its own card.

SAFETY-56 and SAFETY-58 are untouched, as briefed.

Verification

Full suite in the CI image, checks.yml's exact command, a freshly cloned
(purged, 0 __pycache__) tree bind-mounted :ro, one file at a time.

  • baseline b9f7bd0: 30/30 PASS, exit 0
  • this branch eb88646: 30/30 PASS, exit 0

config.yaml's version: and the newest ## heading both read 0.10.1.

Not deployed, not installed, not tagged, and master is untouched.
Shipping to master is a separate step after review.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU

## What this is A version cut, nothing else. **`config.yaml` and `CHANGELOG.md`, and no third file.** `master` and `release/1.0` both say `0.10.0` today. The reference box is running `0.10.0`. The Supervisor decides that an update exists by comparing version strings, so [[SAFETY-57]] — merged at `b9f7bd0` — currently has **no way of reaching the box that has the defect**. The bump is the delivery mechanism, not bookkeeping. That is the whole of this ticket. SAFETY-57 fixes a deadlock that parked the reference battery for about **thirteen minutes on 2026-09-21 while the house exported 2.4 kW**, and which ended only because a human restarted the add-on. ## 1. The cut `config.yaml`: `0.10.0` → `0.10.1`. A **patch**, because SAFETY-57 is a bug fix to a version that has shipped. ⚠️ **The option surface does not move at all** — no key added, none removed, no default changed, no enum widened, no rule added to `validate.py`. So a stored blob that starts the add-on on 0.10.0 starts it on 0.10.1, and there is no `SystemExit(78)` on this hop. That is what makes it a patch rather than a minor, on the same test `## 0.10.0` applies to itself. `CHANGELOG.md` opens a `## 0.10.1` section holding SAFETY-57's entry, moved out of `## 0.10.0`. **Two constraints in `test_version_gate.py` that the brief did not name, and that the new section had to satisfy:** * **part 11a's second control** requires the newest section to carry *exactly one* `in merge order since` contents table. The new section has one, based at `cb3f686` (PKG-14's 0.10.0 cut, read off `git log -L` on the `version:` line rather than assumed). * **part 11c** requires every id in `app/price.py`'s `RETIRED_PROVIDERS` to be named in the **newest** section. Those three ids were named in `## 0.10.0`, which is no longer newest. They are now named in `## 0.10.1` too — which is honest rather than a gate-appeasement, because `ha addons update` installs 0.10.1 in one step and a box older than 0.10.0 still meets that refusal on this hop. The section says so and points at `## 0.10.0` for the detail. 11b (version ↔ newest heading) and 11d (exactly one `## Unreleased` below the newest section — PKG-08's 0.8.0-era block, untouched) both hold. ## 2. What an operator needs in order to decide SAFETY-57's entry opened by explaining that it was misfiled under 0.10.0 and that **whoever cut the next version owned moving it**. This is that cut, so that paragraph is spent. What replaces it is the decision: * **what it does** — the command is parked at **0 W**, the battery-power reading is gated to **MISSING**, and the only condition that would clear the gate is one a parked command cannot produce. A deadlock, not a slow recovery. * **how you recognise it** — the setpoint sits at **exactly 0 W** and does not move while the house is plainly importing or exporting, the battery-power sensor is **flat at one number** throughout (−3 W for 850 s on the live episode), and the action log carries a `STALE batt` line with no clearing line after it. * **what ends it** — **a restart of the add-on**, and a restart is enough: nothing is latched to disk and the pack is not harmed. But nothing ends it by itself. On 0.10.1 the state releases itself after a bounded 32 s. * **what it is not** — not an emergency. 0 W is the safe command. What is lost is the site's own control while it lasts. ## 3. PKG-16 proper — the false preamble `## 0.10.0`'s preamble still claimed 0.10.0 **"has never been built or pulled anywhere"** and that the reference install was on **0.7.0**. Both stopped being true on **2026-09-20 ~22:18 CEST**, when 0.10.0 was installed as a repository add-on (slug `68285ac7_goodwe_controller`) and began regulating a real battery. This text is what the Supervisor shows beside the Update button. Corrected in four places — the "upgrading from 0.7.0, which the reference install is" clause, the `predeploy_check` transcript's framing, the no-op check's "the box reports 0.7.0", and the closing bump argument. The old argument is **retold as history rather than deleted**, because it was stated as conditional and the useful record is that the condition fired. The section now says what the field history bought: a full day-night cycle including an empty pack, a guard trip, a 2 h 47 min saturation freeze, the SAFETY-57 deadlock and a clean recovery — and **two defects that no test in this repository reached**, SAFETY-56 and SAFETY-57. ## 4. The three PR #104 documentation defects Fixed **only where they live in `CHANGELOG.md`**, per the brief. | # | defect | in CHANGELOG | also in (left alone) | |---|---|---|---| | 1446 | the episode is 790 s, three places say 710 s | ✅ fixed | `app/freshness.py:216`, `test_freshness.py:924-938` | | 1447 | keep-alive pulse stated as measurement | ✅ fixed | `app/freshness.py:210`, `test_freshness.py` check name | | 1445 | fixture-bounded "one window" claim | ✅ fixed | `app/freshness.py`, `DOCS.md`, part 10c check name | ⚠️ **1446 was re-derived, not rescaled.** Moving the threshold from 710 s to 790 s moves the count with it: reviewer comment 1446 reports `>= 710 s: 17 of 93` and `>= 790 s: 16 of 93` off the committed capture. The CHANGELOG now says **16 of 93 at or beyond 790 s**. Writing "17 of 93 at 790 s" would have swapped one wrong number for two. ⚠️ **The other occurrences are deliberately untouched.** They are in source, tests and DOCS.md; touching them would make this a third, fourth and fifth file, and none of them is worth delaying a live safety fix. `test_freshness.py` still asserts at the 710.0 threshold, so **the suite and this file now disagree about which number names the episode** — that is a known, stated gap, and it wants its own card. SAFETY-56 and SAFETY-58 are untouched, as briefed. ## Verification Full suite in the CI image, `checks.yml`'s exact command, a freshly cloned (purged, 0 `__pycache__`) tree bind-mounted `:ro`, one file at a time. * **baseline** `b9f7bd0`: 30/30 PASS, exit 0 * **this branch** `eb88646`: 30/30 PASS, exit 0 `config.yaml`'s `version:` and the newest `## ` heading both read `0.10.1`. **Not deployed, not installed, not tagged, and `master` is untouched.** Shipping to `master` is a separate step after review. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
admin added 1 commit 2026-09-22 15:34:04 +02:00
The Supervisor decides an update exists by comparing config.yaml's version
string with the one the box reports. master and release/1.0 both said 0.10.0,
so SAFETY-57 - a deadlock that parked the reference battery for ~13 min on
2026-09-21 while the house exported 2.4 kW - had no way of reaching the box
that has it. A patch bump is the delivery mechanism, not bookkeeping.

- config.yaml: 0.10.0 -> 0.10.1. Nothing else in the option surface moves:
  no key added or removed, no default changed, no enum widened, no rule added
  to validate.py, so a blob that starts on 0.10.0 starts on 0.10.1.

- CHANGELOG.md: a new `## 0.10.1` section, holding SAFETY-57's entry moved out
  of `## 0.10.0`. The entry opened by saying it was misfiled and that whoever
  cut the next version owned moving it; this is that cut, so that paragraph is
  replaced by what an operator actually needs in order to decide - what the
  deadlock does (command parked at 0 W, batt reading gated to MISSING, escape
  condition unreachable from inside the state), how to recognise it (setpoint
  stuck at exactly 0 W with the battery register flat, a STALE line and no
  clearing line) and that a restart of the add-on clears it.

- PKG-16 proper: `## 0.10.0`'s preamble still claimed 0.10.0 "has never been
  built or pulled anywhere" and that the reference install was on 0.7.0. Both
  stopped being true on 2026-09-20 ~22:18 CEST, when 0.10.0 was installed as a
  repository add-on (slug 68285ac7_goodwe_controller) and began regulating a
  real battery. The section's own bump argument was explicitly conditional on
  that; the condition has fired, so the argument is retold as history and the
  section is closed. This text is what the Supervisor shows beside Update.

Also fixes, in this file only, the three non-blocking documentation defects
raised on PR #104 - issuecomments 1445, 1446 and 1447:

- 1446: the live episode is 790 s, not 710 s. Re-derived rather than rescaled:
  at >= 790 s the committed 16-day replay gives 16 of 93, not 17.
- 1447: the BMS keep-alive pulse was an inference stated as a measurement. No
  committed capture identifies one, and on this episode 0x0518 never moved at
  all. The measured claim replaces the firmware story.
- 1445: "neither consumer can reach a verdict inside one window" is universal
  as written and graded on a fixture that puts the obligation back on every
  release. It now carries the qualifier the reviewer asked for, and names the
  unbounded case and why the shipped one-inverter roster cannot reach it.

The same three defects also sit in app/freshness.py, test_freshness.py,
DOCS.md and the DECISIONS note. They are deliberately NOT touched here: this
PR is config.yaml and CHANGELOG.md only, and none of them is worth delaying a
live safety fix. SAFETY-56 and SAFETY-58 are likewise untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
Author
Owner

Review 1/7 — VERIFIED: the identity holds, so this presents as an update and not as a new add-on

This is the check the whole ticket lives or dies on, so I did not read it off the diff stat.

config.yaml moves one line, and it is the right one:

-version: "0.10.0"
+version: "0.10.1"

Everything the Supervisor uses to decide which installed add-on this is is byte-identical across b9f7bd0..eb88646:

slug: goodwe_controller — unchanged
name: GoodWe RS485 Controller — unchanged
url: unchanged
add-on directory goodwe_controller/ — unchanged, no rename in --name-status
repository.yaml not in the diff at all

And I corroborated the installed identity without going near the box. /home/homelab/ha_backup/.910d1f5e.metadata.json (automatic backup, 2026-09-22T05:23:28+02:00) lists:

{'name': 'GoodWe RS485 Controller', 'slug': '68285ac7_goodwe_controller', 'version': '0.10.0'}
{'name': 'GoodWe RS485 Controller', 'slug': 'local_goodwe_controller',    'version': '0.7.0'}

The installed slug is <repo-hash>_<config slug> = 68285ac7 + goodwe_controller. Both halves are untouched by this PR, so the bump lands on the same add-on row the box already has.

One more thing I checked because it would have been silent: config.yaml has no image: key, so this is a local-build add-on. The Supervisor builds from the Dockerfile on the box; there is no registry tag that has to exist for 0.10.1 before the Update button works. build.yaml's base tags are untouched.

The concrete failure this rules out: a moved slug, a renamed directory, or a changed repository URL would have made the Supervisor offer this as a second, new add-on rather than an update — new /data, empty options, and the owner re-entering their entity ids at night, which is exactly what the local_goodwe_controller → 68285ac7_goodwe_controller swap already cost them once. None of that is in this diff.

## Review 1/7 — VERIFIED: the identity holds, so this presents as an *update* and not as a new add-on This is the check the whole ticket lives or dies on, so I did not read it off the diff stat. `config.yaml` moves **one line**, and it is the right one: ``` -version: "0.10.0" +version: "0.10.1" ``` Everything the Supervisor uses to decide *which installed add-on this is* is byte-identical across `b9f7bd0..eb88646`: | | | |---|---| | `slug:` | `goodwe_controller` — unchanged | | `name:` | `GoodWe RS485 Controller` — unchanged | | `url:` | unchanged | | add-on directory | `goodwe_controller/` — unchanged, no rename in `--name-status` | | `repository.yaml` | not in the diff at all | **And I corroborated the installed identity without going near the box.** `/home/homelab/ha_backup/.910d1f5e.metadata.json` (automatic backup, 2026-09-22T05:23:28+02:00) lists: ``` {'name': 'GoodWe RS485 Controller', 'slug': '68285ac7_goodwe_controller', 'version': '0.10.0'} {'name': 'GoodWe RS485 Controller', 'slug': 'local_goodwe_controller', 'version': '0.7.0'} ``` The installed slug is `<repo-hash>_<config slug>` = `68285ac7` + `goodwe_controller`. Both halves are untouched by this PR, so the bump lands on **the same add-on row the box already has**. One more thing I checked because it would have been silent: `config.yaml` has **no `image:` key**, so this is a local-build add-on. The Supervisor builds from the Dockerfile on the box; there is no registry tag that has to exist for `0.10.1` before the Update button works. `build.yaml`'s base tags are untouched. **The concrete failure this rules out:** a moved slug, a renamed directory, or a changed repository URL would have made the Supervisor offer this as a *second, new* add-on rather than an update — new `/data`, empty options, and the owner re-entering their entity ids at night, which is exactly what the `local_goodwe_controller` → `68285ac7_goodwe_controller` swap already cost them once. None of that is in this diff.
Author
Owner

Review 2/7 — ⚠️ NON-BLOCKING FOR THIS PR, BUT THE DELIVERY IS NOT DONE AT MERGE: the Supervisor reads master, and master is still 0.10.0

I am flagging this at the top of the stack because it is the one way tonight still goes wrong with this PR merged and green.

The Supervisor clones the add-on repository's default branch. I asked the forge rather than assuming:

GET /api/v1/repos/admin/goodwe-addon  ->  "default_branch": "master"

and the remote refs right now:

6f5e8e3  refs/heads/master          goodwe_controller/config.yaml -> version: "0.10.0"
b9f7bd0  refs/heads/release/1.0
eb88646  refs/heads/PKG-16-release-0.10.1   (this PR's head)

master is at 0.10.0 and does not contain SAFETY-57 either — b9f7bd0 and 43c144b are on release/1.0 only. So merging #105 into release/1.0 moves the box not at all: it will go on reporting 0.10.0, go on matching master's 0.10.0, and go on running the deadlock.

The PR body says this plainly ("Shipping to master is a separate step after review", "master is untouched"), and it is the correct scope for a cut. This is not a blocker and I am not asking for a change here. It is a hand-off: the act that actually arms the Update button is bringing master to release/1.0, the same move 6f5e8e3 ("PKG-15: bring release/1.0 to master at 0.10.0, so the default branch is the add-on") made last time.

The good news, checked: git log --oneline origin/master ^origin/release/1.0 is empty, so master is a strict ancestor of release/1.0 and that ship is a fast-forward, with no merge and nothing to resolve.

The concrete failure it implies if it is forgotten: a merged PR, a green gate, a tidy ## 0.10.1 section — and a box that believes it is current, with a 13-minute battery-parking deadlock still armed. That is the PKG-06/PKG-07 no-op one level up: the bump exists, but not on the branch anybody reads.

## Review 2/7 — ⚠️ NON-BLOCKING FOR THIS PR, BUT THE DELIVERY IS **NOT** DONE AT MERGE: the Supervisor reads `master`, and `master` is still 0.10.0 I am flagging this at the top of the stack because it is the one way tonight still goes wrong with this PR merged and green. The Supervisor clones the add-on repository's **default branch**. I asked the forge rather than assuming: ``` GET /api/v1/repos/admin/goodwe-addon -> "default_branch": "master" ``` and the remote refs right now: ``` 6f5e8e3 refs/heads/master goodwe_controller/config.yaml -> version: "0.10.0" b9f7bd0 refs/heads/release/1.0 eb88646 refs/heads/PKG-16-release-0.10.1 (this PR's head) ``` **`master` is at 0.10.0 and does not contain SAFETY-57 either** — `b9f7bd0` and `43c144b` are on `release/1.0` only. So merging #105 into `release/1.0` moves the box not at all: it will go on reporting `0.10.0`, go on matching `master`'s `0.10.0`, and go on running the deadlock. The PR body says this plainly ("Shipping to `master` is a separate step after review", "`master` is untouched"), and it is the correct scope for a cut. **This is not a blocker and I am not asking for a change here.** It is a hand-off: the act that actually arms the Update button is bringing `master` to `release/1.0`, the same move `6f5e8e3` ("PKG-15: bring release/1.0 to master at 0.10.0, so the default branch is the add-on") made last time. **The good news, checked:** `git log --oneline origin/master ^origin/release/1.0` is **empty**, so `master` is a strict ancestor of `release/1.0` and that ship is a **fast-forward**, with no merge and nothing to resolve. **The concrete failure it implies if it is forgotten:** a merged PR, a green gate, a tidy `## 0.10.1` section — and a box that believes it is current, with a 13-minute battery-parking deadlock still armed. That is the PKG-06/PKG-07 no-op one level up: the bump exists, but not on the branch anybody reads.
Author
Owner

Review 3/7 — VERIFIED: 11a, 11b, 11c and 11d all hold, re-implemented rather than taken from the suite's green

A passing test_version_gate.py tells me the gate is satisfied; it does not tell me why, and part 11's whole design is that a satisfied gate can still be a lie (its own comments say so — CFG-04's "asserted against the TEXT of the thing under test"). So I re-implemented the slicer and the AST read against the branch tree and ran them outside the suite:

headings: ['0.10.1', '0.10.0', '0.9.0', 'Unreleased', '0.8.0', '0.7.0', ...]
newest heading: '0.10.1'  (168 lines)

11a  sentinel '## 0.8.0' in file: True | in newest slice: False   -> slicer closes
11a  contents tables in newest: 1
       ['Contents of this release, in merge order since 0.10.0 (`cb3f686`):']
11b  config.yaml version: '0.10.1' == newest heading: True | not 'unreleased': True
11c  RETIRED_PROVIDERS (AST off app/price.py): ['eneco_eddy','engie_dynamic','totalenergies_dynamic']
11c  unnamed in newest section: []
11d  '## Unreleased' blocks below newest: 1

All four hold. I also checked the base of the contents table rather than accepting it: git log -L 2,2:goodwe_controller/config.yaml walks eb88646 -> cb3f686 -> 86bdf49 -> ..., and cb3f686 is PKG-14: cut 0.10.0 (2026-09-16). So "in merge order since 0.10.0 (cb3f686)" names the real previous bump, read off the version: line's own history, not guessed.

And I checked the table's contents, since a one-row table is the kind of claim that is easy to make and easy to get wrong. cb3f686..b9f7bd0 carries TEL-10, SAFETY-48, CI-9, PKG-15 and SAFETY-57. TEL-10 and SAFETY-48 were folded into ## 0.10.0 by PKG-15 (5aba220/8a57361) before the install, CI-9 and PKG-15 touch checks.yml and CHANGELOG.md only. SAFETY-57 is correctly the only operator-observable row. The section says all of that out loud.

On 11c specifically — is the retired-provider text honest, or is it gate-appeasement?

This was the one I expected to find bent, because 11c is the rule with the most obvious cheap answer: paste three ids into the newest section and go green. It is not bent. The text is scoped:

⚠️ IF YOU ARE CROSSING FROM A VERSION OLDER THAN 0.10.0, THIS SECTION IS NOT THE ONE THAT CAN REFUSE YOU - ## 0.10.0 BELOW IS.

That is the truthful shape, and it is truthful specifically for the reader this release is aimed at. A box on 0.10.0 already cleared TARIFF-07's three ids and SAFETY-39's rule 10 at startup when it installed 0.10.0; nothing on the 0.10.0 → 0.10.1 hop can refuse it, and the section's opening paragraph says exactly that ("there is no SystemExit(78) to plan around on this hop"). I confirmed that is structurally true and not just asserted: the diff is two files, app/validate.py and the options:/schema: blocks are untouched, so the option surface genuinely cannot move.

And the claim it does make about older boxes is true too — app/validate.py:1298 still refuses pid in RETIRED_PROVIDERS and still builds the message from RETIRED_PROVIDERS[pid], unchanged by this diff, so "still refused by name at startup on this version, with the same message" is accurate. The pointer at ## 0.10.0 for the detail is the right call rather than duplicating it.

The concrete failure this rules out: three ids dropped into a section to silence a check, on a hop where the section's own reader cannot meet them — which would have made the newest release note a place where text goes to satisfy a test instead of a reader. That is not what happened here.

## Review 3/7 — VERIFIED: 11a, 11b, 11c and 11d all hold, re-implemented rather than taken from the suite's green A passing `test_version_gate.py` tells me the gate is satisfied; it does not tell me *why*, and part 11's whole design is that a satisfied gate can still be a lie (its own comments say so — CFG-04's "asserted against the TEXT of the thing under test"). So I re-implemented the slicer and the AST read against the branch tree and ran them outside the suite: ``` headings: ['0.10.1', '0.10.0', '0.9.0', 'Unreleased', '0.8.0', '0.7.0', ...] newest heading: '0.10.1' (168 lines) 11a sentinel '## 0.8.0' in file: True | in newest slice: False -> slicer closes 11a contents tables in newest: 1 ['Contents of this release, in merge order since 0.10.0 (`cb3f686`):'] 11b config.yaml version: '0.10.1' == newest heading: True | not 'unreleased': True 11c RETIRED_PROVIDERS (AST off app/price.py): ['eneco_eddy','engie_dynamic','totalenergies_dynamic'] 11c unnamed in newest section: [] 11d '## Unreleased' blocks below newest: 1 ``` All four hold. I also checked the **base** of the contents table rather than accepting it: `git log -L 2,2:goodwe_controller/config.yaml` walks `eb88646 -> cb3f686 -> 86bdf49 -> ...`, and `cb3f686` is `PKG-14: cut 0.10.0` (2026-09-16). So "in merge order since 0.10.0 (`cb3f686`)" names the real previous bump, read off the `version:` line's own history, not guessed. And I checked the table's **contents**, since a one-row table is the kind of claim that is easy to make and easy to get wrong. `cb3f686..b9f7bd0` carries TEL-10, SAFETY-48, CI-9, PKG-15 and SAFETY-57. TEL-10 and SAFETY-48 were folded into `## 0.10.0` by PKG-15 (`5aba220`/`8a57361`) *before* the install, CI-9 and PKG-15 touch `checks.yml` and `CHANGELOG.md` only. SAFETY-57 is correctly the only operator-observable row. The section says all of that out loud. ### On 11c specifically — is the retired-provider text honest, or is it gate-appeasement? This was the one I expected to find bent, because 11c is the rule with the most obvious cheap answer: paste three ids into the newest section and go green. It is not bent. The text is scoped: > ⚠️ **IF YOU ARE CROSSING FROM A VERSION OLDER THAN 0.10.0, THIS SECTION IS NOT THE ONE THAT CAN REFUSE YOU - `## 0.10.0` BELOW IS.** That is the truthful shape, and it is truthful specifically **for the reader this release is aimed at**. A box on 0.10.0 already cleared TARIFF-07's three ids and SAFETY-39's rule 10 at startup when it installed 0.10.0; nothing on the 0.10.0 → 0.10.1 hop can refuse it, and the section's opening paragraph says exactly that ("there is no `SystemExit(78)` to plan around on this hop"). I confirmed that is structurally true and not just asserted: the diff is two files, `app/validate.py` and the `options:`/`schema:` blocks are untouched, so the option surface genuinely cannot move. And the claim it *does* make about older boxes is true too — `app/validate.py:1298` still refuses `pid in RETIRED_PROVIDERS` and still builds the message from `RETIRED_PROVIDERS[pid]`, unchanged by this diff, so "still refused by name at startup on this version, with the same message" is accurate. The pointer at `## 0.10.0` for the detail is the right call rather than duplicating it. **The concrete failure this rules out:** three ids dropped into a section to silence a check, on a hop where the section's own reader cannot meet them — which would have made the newest release note a place where text goes to satisfy a test instead of a reader. That is not what happened here.
Author
Owner

Review 4/7 — VERIFIED BY RE-DERIVATION: 16 of 93 at or beyond 790 s is the correct pairing, and it is the pairing a find-and-replace would have got wrong

This is the number I trusted least on the way in, because the threshold and the count move together and the temptation to sed 710 → 790 and leave 17 alone is enormous.

I did not take it from the PR body or from #104 issuecomment 1446. I replayed the committed capture myself: branch tree at eb88646, test_freshness.py part 8b, in goodwe-addon-checks:1, with one extra print added to the replay loop and nothing else changed:

16 days replayed at 1 Hz: 93 trips
wait for the register's next change after a trip:
  min 0.6 s  median 143.4 s  max 21440.1 s
REVIEW-DERIVE: n=93  >=710: 17   >=790: 16   >=815: 15   >=850: 15

That reproduces issuecomment 1446's table exactly, off the same committed capture, and it confirms the CHANGELOG's new sentence:

93 trips whose wait has a median of 143 s and a worst case of 5 h 57 m, with 16 of the 93 at or beyond this episode's 790 s

17 of 93 at 790 s would have been wrong, and 16 of 93 at 710 s would have been wrong. The branch has the one pairing that is right. The re-derivation is the reason I am able to say that rather than assume it.

One nuance, stated because it is real, and explicitly NOT a request for a change

The replay distribution measures time from trip to the register's next change — the clock that produced the episode's 850 s figure. The threshold it is now compared against, 790 s, is the setpoint hold. So strictly the sentence compares a register-wait distribution against a setpoint-derived number, and 815 s (the verdict, per 1446) is a third clock again.

I am satisfied this is the right choice anyway, and it is the one #104's own reviewer derived and labelled "the episode's actual parked setpoint". 790 s is the operator-meaningful quantity — how long the site was actually parked — so "16 of 93 historical trips would have parked this site at least as long as the live episode did" is the sentence a reader wants. The conclusion is also invariant across all three: 17, 16 and 15 of 93 all say the same thing, which is that the old exit was neither rare nor short.

No change requested on this point. Naming it so the record shows it was looked at and not missed.

## Review 4/7 — VERIFIED BY RE-DERIVATION: `16 of 93 at or beyond 790 s` is the correct pairing, and it is the pairing a find-and-replace would have got wrong This is the number I trusted least on the way in, because the threshold and the count move together and the temptation to sed `710` → `790` and leave `17` alone is enormous. I did not take it from the PR body or from #104 issuecomment 1446. I replayed the committed capture myself: branch tree at `eb88646`, `test_freshness.py` part 8b, in `goodwe-addon-checks:1`, with one extra print added to the replay loop and nothing else changed: ``` 16 days replayed at 1 Hz: 93 trips wait for the register's next change after a trip: min 0.6 s median 143.4 s max 21440.1 s REVIEW-DERIVE: n=93 >=710: 17 >=790: 16 >=815: 15 >=850: 15 ``` That reproduces issuecomment 1446's table exactly, off the same committed capture, and it confirms the CHANGELOG's new sentence: > **93 trips** whose wait has a **median of 143 s** and a worst case of **5 h 57 m**, with **16 of the 93** at or beyond this episode's **790 s** **`17 of 93 at 790 s` would have been wrong, and `16 of 93 at 710 s` would have been wrong.** The branch has the one pairing that is right. The re-derivation is the reason I am able to say that rather than assume it. ### One nuance, stated because it is real, and explicitly NOT a request for a change The replay distribution measures *time from trip to the register's next change* — the clock that produced the episode's **850 s** figure. The threshold it is now compared against, **790 s**, is the *setpoint* hold. So strictly the sentence compares a register-wait distribution against a setpoint-derived number, and 815 s (the verdict, per 1446) is a third clock again. I am satisfied this is the right choice anyway, and it is the one #104's own reviewer derived and labelled "the episode's actual parked setpoint". 790 s is the operator-meaningful quantity — how long the site was actually parked — so "16 of 93 historical trips would have parked this site at least as long as the live episode did" is the sentence a reader wants. The conclusion is also invariant across all three: 17, 16 and 15 of 93 all say the same thing, which is that the old exit was neither rare nor short. **No change requested on this point.** Naming it so the record shows it was looked at and not missed.
Author
Owner

Review 5/7 — the 710 s / 790 s split between the suite and this file: I agree it does not block, and here is the reasoning rather than the verdict

The gap is real and the PR flagged it rather than hiding it, which is the only reason it is cheap to judge. On eb88646:

  • CHANGELOG.md — 16 of 93 at or beyond 790 s (fixed here)
  • test_freshness.py:920-938 — asserts sum(w >= 710.0) == 17, and its check name reads "at or beyond the 710 s the live site was parked for on 2026-09-21"
  • app/freshness.py:216 — "17 episodes at or over the live one's 710 s"

Why I do not think it blocks a live safety fix tonight:

  1. The two statements are not in contradiction. They are both true. >=710: 17 and >=790: 16 are the same distribution read at two thresholds — I re-derived both (review 4/7). The suite's assertion is numerically correct at its own threshold and passes honestly. Nothing in it can go green on a tree where it should be red.
  2. The defect is an attribution in prose, not a number in a gate. What is wrong is the clause "the 710 s the live site was parked for" — the site was parked 790 s. That is a wrong sentence attached to a right assertion. It cannot produce a wrong verdict, cannot mis-grade a future change, and cannot reach an operator: freshness.py's docstring and a check name are not operator-facing surfaces. The one surface that is operator-facing — the text beside the Update button — is the one this PR fixed.
  3. The cost of fixing it here is a third file and a fourth, on a cut whose entire value is that it is two files, on the night the fix is meant to reach a box with the deadlock armed. That trade is not close.

So: agreed, own card, merge.

One addition for that card — the PR's table is one site short

The table in the PR body lists three sites for defect 1446, following issuecomment 1446. There is a fourth on this tree:

DECISIONS/SAFETY-57-the-exit-from-a-stale-verdict.md:274
    | at or over the live episode's 710 s | **17 of 93** |

Nothing to do here — it is out of scope for exactly the reason the other three are. But a follow-up card written from the PR's table alone would fix three places and leave the decision record still saying 710, which is the same drift one iteration later. Four sites, not three.

The concrete failure this comment guards against: a follow-up card that closes 3/4 of a documentation drift and declares it closed.

## Review 5/7 — the 710 s / 790 s split between the suite and this file: **I agree it does not block, and here is the reasoning rather than the verdict** The gap is real and the PR flagged it rather than hiding it, which is the only reason it is cheap to judge. On `eb88646`: * `CHANGELOG.md` — **16 of 93 at or beyond 790 s** (fixed here) * `test_freshness.py:920-938` — asserts `sum(w >= 710.0) == 17`, and its check name reads *"at or beyond the **710 s** the live site was parked for on 2026-09-21"* * `app/freshness.py:216` — *"17 episodes at or over the live one's **710 s**"* **Why I do not think it blocks a live safety fix tonight:** 1. **The two statements are not in contradiction. They are both true.** `>=710: 17` and `>=790: 16` are the same distribution read at two thresholds — I re-derived both (review 4/7). The suite's assertion is *numerically correct at its own threshold* and passes honestly. Nothing in it can go green on a tree where it should be red. 2. **The defect is an attribution in prose, not a number in a gate.** What is wrong is the clause *"the 710 s the live site was parked for"* — the site was parked 790 s. That is a wrong sentence attached to a right assertion. It cannot produce a wrong verdict, cannot mis-grade a future change, and cannot reach an operator: `freshness.py`'s docstring and a check name are not operator-facing surfaces. The one surface that *is* operator-facing — the text beside the Update button — is the one this PR fixed. 3. **The cost of fixing it here is a third file and a fourth**, on a cut whose entire value is that it is two files, on the night the fix is meant to reach a box with the deadlock armed. That trade is not close. So: **agreed, own card, merge.** ### One addition for that card — the PR's table is one site short The table in the PR body lists three sites for defect 1446, following issuecomment 1446. There is a fourth on this tree: ``` DECISIONS/SAFETY-57-the-exit-from-a-stale-verdict.md:274 | at or over the live episode's 710 s | **17 of 93** | ``` Nothing to do here — it is out of scope for exactly the reason the other three are. But a follow-up card written from the PR's table alone would fix three places and leave the decision record still saying 710, which is the same drift one iteration later. **Four sites, not three.** **The concrete failure this comment guards against:** a follow-up card that closes 3/4 of a documentation drift and declares it closed.
Author
Owner

Review 6/7 — VERIFIED (with one limit I cannot clear): the corrected preamble is true, and the correction is the right shape

The old text was not merely stale, it was the reverse of the truth — it told an operator, beside the Update button, that 0.10.0 "has never been built or pulled anywhere" and that the reference install was on 0.7.0. Both false since 2026-09-20.

What I could verify, without contacting the box:

/home/homelab/ha_backup/.910d1f5e.metadata.json (automatic backup, 2026-09-22T05:23:28+02:00) and .a1b8197b.metadata.json (2026-09-21T05:38:42+02:00) both list:

{'name': 'GoodWe RS485 Controller', 'slug': '68285ac7_goodwe_controller', 'version': '0.10.0'}

So, confirmed independently of the PR's own assertions:

  • 0.10.0 has been built and pulled — it is installed;
  • it is installed as a repository add-on, under precisely the slug the corrected text names, 68285ac7_goodwe_controller;
  • the reference install is on 0.10.0, not 0.7.0;
  • and it was already so by the morning of 2026-09-21, which is consistent with an install on the evening of 2026-09-20.

The tree agrees from its own side: DECISIONS/SAFETY-57-...md:93 ("the box was running 0.10.0") and :377 ("0.10.0 is on the reference install"), and test_freshness.py:1202 ("running 0.10.0, i.e. running the defect").

What I could NOT verify, and am recording as such rather than nodding it through: the claim is "2026-09-20 at about 22:18 CEST". The earliest artefact I have is the 2026-09-21 05:38 backup, which already shows 0.10.0. So I have corroborated the install to the day, not to the minute. The 22:18 timestamp rests on the operator's own record. Nothing in the correction's argument depends on the minute, so I am not treating this as a finding — but it should not be cited later as something review confirmed.

On the shape of the correction, which I think is the better half of it: the old argument is retold as history and then closed, not deleted —

⚠️ IT WAS STATED AS CONDITIONAL ON THAT REMAINING TRUE, AND IT STOPPED BEING TRUE ON 2026-09-20...

That is right. The 0.10.0 argument was explicitly conditional and the condition fired; deleting it would destroy the record that it was conditional, and a reader would be left unable to tell whether the earlier fold was a mistake or a decision whose premise expired. The four corrected sites (the "upgrading from 0.7.0" clause, the predeploy_check transcript framing, the no-op check's "the box reports 0.7.0", and the closing bump argument) are the four places the falsehood actually lived — I diffed for others and found none left in ## 0.10.0.

The field-history paragraph — a full day-night cycle, an empty pack, a guard trip, a 2 h 47 min saturation freeze, the SAFETY-57 deadlock, a clean recovery, and two defects no test in this repository reached (SAFETY-56, SAFETY-57) — I can confirm for SAFETY-56 and SAFETY-57 (both named on this tree, both field-found, neither reproduced by a pre-existing test). The finer operational details of that cycle are the operator's record and are not checkable from here.

The concrete failure this closes: an operator reading the release notes beside the Update button and being told the release they are already running has never been deployed — which would undermine every other claim on the page at the moment they are deciding whether to trust it.

## Review 6/7 — VERIFIED (with one limit I cannot clear): the corrected preamble is true, and the correction is the right shape The old text was not merely stale, it was the reverse of the truth — it told an operator, beside the Update button, that 0.10.0 *"has never been built or pulled anywhere"* and that the reference install was on 0.7.0. Both false since 2026-09-20. **What I could verify, without contacting the box:** `/home/homelab/ha_backup/.910d1f5e.metadata.json` (automatic backup, 2026-09-22T05:23:28+02:00) and `.a1b8197b.metadata.json` (2026-09-21T05:38:42+02:00) both list: ``` {'name': 'GoodWe RS485 Controller', 'slug': '68285ac7_goodwe_controller', 'version': '0.10.0'} ``` So, confirmed independently of the PR's own assertions: * 0.10.0 **has** been built and pulled — it is installed; * it is installed **as a repository add-on**, under precisely the slug the corrected text names, `68285ac7_goodwe_controller`; * the reference install is on **0.10.0, not 0.7.0**; * and it was already so by the morning of **2026-09-21**, which is consistent with an install on the evening of 2026-09-20. The tree agrees from its own side: `DECISIONS/SAFETY-57-...md:93` ("the box was running 0.10.0") and `:377` ("0.10.0 is on the reference install"), and `test_freshness.py:1202` ("running 0.10.0, i.e. running the defect"). **What I could NOT verify, and am recording as such rather than nodding it through:** the claim is *"2026-09-20 at about 22:18 CEST"*. The earliest artefact I have is the 2026-09-21 05:38 backup, which already shows 0.10.0. So I have corroborated the install **to the day, not to the minute**. The 22:18 timestamp rests on the operator's own record. Nothing in the correction's argument depends on the minute, so I am not treating this as a finding — but it should not be cited later as something review confirmed. **On the shape of the correction, which I think is the better half of it:** the old argument is *retold as history and then closed*, not deleted — > ⚠️ **IT WAS STATED AS CONDITIONAL ON THAT REMAINING TRUE, AND IT STOPPED BEING TRUE ON 2026-09-20...** That is right. The 0.10.0 argument was explicitly conditional and the condition fired; deleting it would destroy the record that it was conditional, and a reader would be left unable to tell whether the earlier fold was a mistake or a decision whose premise expired. The four corrected sites (the "upgrading from 0.7.0" clause, the `predeploy_check` transcript framing, the no-op check's "the box reports 0.7.0", and the closing bump argument) are the four places the falsehood actually lived — I diffed for others and found none left in `## 0.10.0`. The field-history paragraph — a full day-night cycle, an empty pack, a guard trip, a 2 h 47 min saturation freeze, the SAFETY-57 deadlock, a clean recovery, and **two defects no test in this repository reached (SAFETY-56, SAFETY-57)** — I can confirm for SAFETY-56 and SAFETY-57 (both named on this tree, both field-found, neither reproduced by a pre-existing test). The finer operational details of that cycle are the operator's record and are not checkable from here. **The concrete failure this closes:** an operator reading the release notes beside the Update button and being told the release they are already running has never been deployed — which would undermine every other claim on the page at the moment they are deciding whether to trust it.
Author
Owner

Review 7/7 — scope, the faithfulness of the move, the operator-decision text, and the gate. Merging.

The entry was MOVED, not rewritten — checked line by line

A section relocated between releases is the perfect cover for a silent edit, so I extracted ### SAFETY-57 … from b9f7bd0 and from eb88646 and diffed the two in isolation. Four changes, all four declared in the PR body, and nothing else:

  1. the opening misfiling paragraph replaced by the operator-decision text;
  2. 1447 — the BMS keep-alive pulse, previously stated as measured fact, now explicitly retracted: "no committed capture identifies such a pulse", replaced by what is measured;
  3. 1446 — 710 s / 17 → 790 s / 16 (re-derived in review 4/7);
  4. 1445 — the fixture-bounded "one window" claim now carries the "IT IS NOT A UNIVERSAL BOUND" paragraph and names it a stated gap.

No other line of that entry moved. The three #104 documentation defects are fixed where they live in CHANGELOG.md and nowhere else, as briefed.

The operator-decision text answers the question it exists to answer

Someone on 0.10.0 can decide from it alone: what it does (command parked at 0 W, reading gated to MISSING, the only clearing condition one a parked command cannot produce — "a deadlock, not a slow recovery"); how to recognise it (setpoint at exactly 0 W and not moving while the house is plainly importing or exporting; battery-power sensor flat at one number, −3 W for 850 s on the live episode; a STALE batt line with no clearing line after it); what ends it (a restart, and a restart is enough — nothing latched to disk, the pack unharmed); and what it is not (not an emergency — 0 W is the safe command; what is lost is the site's own control). All three recognition signals asked for are present, and the "not an emergency" framing is the part that stops a 22:00 update becoming a panic.

Scope: clean

$ git diff --name-status b9f7bd05 eb886468
M  goodwe_controller/CHANGELOG.md   (+202/-94)
M  goodwe_controller/config.yaml    (+1/-1)

Two files. No control code, no app/, no tests, no workflows. merge-base is b9f7bd0 itself, so the branch is a clean single commit on the base tip. SAFETY-56 and SAFETY-58 are untouched (named in prose as excluded, which is correct — they are open, and neither was allowed to delay this). The source-comment copies of the three #104 defects are deliberately left alone, per the brief.

The gate

Full suite reproduced myself — checks.yml's exact command, goodwe-addon-checks:1, freshly extracted tree (0 __pycache__), bind-mounted :ro, one file at a time:

30 test file(s) to run
PASS  test_alloc.py … PASS  test_version_gate.py (10s) … PASS  test_watchdog.py (66s)
30/30, exit 0

And I checked the CI status on the head is a real run and not a CI-9 laundered skip, since checks.yml documents that hazard at length:

success | checks / unit (pull_request) | Successful in 8m0s | .../actions/runs/812/jobs/1936
rollup: success, total_count 1

A job, a duration and a URL — not {"status":"skipped","target_url":""}.


Verdict: sound. Merging.

Seven findings, none blocking. The one thing that still stands between the owner and the fix is review 2/7 — this merge does not move master, and master is what the Supervisor reads. That is a hand-off, not a defect in this PR.

## Review 7/7 — scope, the faithfulness of the move, the operator-decision text, and the gate. **Merging.** ### The entry was MOVED, not rewritten — checked line by line A section relocated between releases is the perfect cover for a silent edit, so I extracted `### SAFETY-57 …` from `b9f7bd0` and from `eb88646` and diffed the two in isolation. Four changes, all four declared in the PR body, and **nothing else**: 1. the opening misfiling paragraph replaced by the operator-decision text; 2. **1447** — the BMS keep-alive pulse, previously stated as measured fact, now explicitly retracted: *"no committed capture identifies such a pulse"*, replaced by what **is** measured; 3. **1446** — 710 s / 17 → 790 s / 16 (re-derived in review 4/7); 4. **1445** — the fixture-bounded "one window" claim now carries the *"IT IS NOT A UNIVERSAL BOUND"* paragraph and names it a stated gap. No other line of that entry moved. The three #104 documentation defects are fixed where they live in `CHANGELOG.md` and nowhere else, as briefed. ### The operator-decision text answers the question it exists to answer Someone on 0.10.0 can decide from it alone: **what it does** (command parked at 0 W, reading gated to MISSING, the only clearing condition one a parked command cannot produce — "a deadlock, not a slow recovery"); **how to recognise it** (setpoint at *exactly* 0 W and not moving while the house is plainly importing or exporting; battery-power sensor flat at one number, −3 W for 850 s on the live episode; a `STALE batt` line with no clearing line after it); **what ends it** (a restart, and a restart is enough — nothing latched to disk, the pack unharmed); and **what it is not** (not an emergency — 0 W is the safe command; what is lost is the site's own control). All three recognition signals asked for are present, and the "not an emergency" framing is the part that stops a 22:00 update becoming a panic. ### Scope: clean ``` $ git diff --name-status b9f7bd05 eb886468 M goodwe_controller/CHANGELOG.md (+202/-94) M goodwe_controller/config.yaml (+1/-1) ``` Two files. **No control code, no `app/`, no tests, no workflows.** `merge-base` is `b9f7bd0` itself, so the branch is a clean single commit on the base tip. SAFETY-56 and SAFETY-58 are untouched (named in prose as excluded, which is correct — they are open, and neither was allowed to delay this). The source-comment copies of the three #104 defects are deliberately left alone, per the brief. ### The gate Full suite reproduced myself — `checks.yml`'s exact command, `goodwe-addon-checks:1`, freshly extracted tree (`0` `__pycache__`), bind-mounted `:ro`, one file at a time: ``` 30 test file(s) to run PASS test_alloc.py … PASS test_version_gate.py (10s) … PASS test_watchdog.py (66s) 30/30, exit 0 ``` And I checked the CI status on the head is a **real run and not a CI-9 laundered skip**, since `checks.yml` documents that hazard at length: ``` success | checks / unit (pull_request) | Successful in 8m0s | .../actions/runs/812/jobs/1936 rollup: success, total_count 1 ``` A job, a duration and a URL — not `{"status":"skipped","target_url":""}`. --- ### Verdict: **sound. Merging.** Seven findings, **none blocking**. The one thing that still stands between the owner and the fix is review 2/7 — this merge does not move `master`, and `master` is what the Supervisor reads. That is a hand-off, not a defect in this PR.
admin merged commit b0b24e5d41 into release/1.0 2026-09-22 15:52:26 +02:00
Author
Owner

MERGED — release/1.0 is now b0b24e5d4139fa2bc5cc4828549c6d67b29c7ae1

Confirmed from the remote, not from the merge call's own 200:

$ git ls-remote origin refs/heads/release/1.0 refs/heads/master
b0b24e5d4139fa2bc5cc4828549c6d67b29c7ae1  refs/heads/release/1.0
6f5e8e378a54d44593b1092fd2180a7a78fc4eaa  refs/heads/master        <- unchanged

$ git log -1 --format='%H %p%n%s' origin/release/1.0
b0b24e5... parents: b9f7bd0 eb88646
PKG-16: cut 0.10.1, so SAFETY-57's fix can reach an installed 0.10.0 (#105)

$ git show origin/release/1.0:goodwe_controller/config.yaml | head -3
name: GoodWe RS485 Controller
version: "0.10.1"
slug: goodwe_controller

$ git show origin/release/1.0:goodwe_controller/CHANGELOG.md | grep -m1 '^## '
## 0.10.1

API: merged: True, merge_commit_sha: b0b24e5…, changed_files: 2.

And the merge commit's tree is byte-identical to the head I tested — b0b24e5^{tree} == eb88646^{tree}, git diff b0b24e5 eb88646 empty — so the 30/30 I reproduced is a claim about exactly the content that landed, not about a tree that no longer exists.


⚠️ The fix has not reached the box yet, and this merge is not what sends it

Repeating review 2/7 here so it is the last thing on the thread rather than the second:

master is the Supervisor's default branch, it is still 6f5e8e3 at 0.10.0, and it still does not contain SAFETY-57. Until master moves, the box goes on reporting 0.10.0, goes on matching master, and goes on running the deadlock — with a merged PR and a green gate above it.

master is a strict ancestor of release/1.0, so that ship is a fast-forward to b0b24e5, exactly as 6f5e8e3 did for 0.10.0. Nothing to resolve.

Nothing was deployed, installed, or tagged by this review.

## MERGED — `release/1.0` is now `b0b24e5d4139fa2bc5cc4828549c6d67b29c7ae1` Confirmed from the **remote**, not from the merge call's own 200: ``` $ git ls-remote origin refs/heads/release/1.0 refs/heads/master b0b24e5d4139fa2bc5cc4828549c6d67b29c7ae1 refs/heads/release/1.0 6f5e8e378a54d44593b1092fd2180a7a78fc4eaa refs/heads/master <- unchanged $ git log -1 --format='%H %p%n%s' origin/release/1.0 b0b24e5... parents: b9f7bd0 eb88646 PKG-16: cut 0.10.1, so SAFETY-57's fix can reach an installed 0.10.0 (#105) $ git show origin/release/1.0:goodwe_controller/config.yaml | head -3 name: GoodWe RS485 Controller version: "0.10.1" slug: goodwe_controller $ git show origin/release/1.0:goodwe_controller/CHANGELOG.md | grep -m1 '^## ' ## 0.10.1 ``` API: `merged: True`, `merge_commit_sha: b0b24e5…`, `changed_files: 2`. **And the merge commit's tree is byte-identical to the head I tested** — `b0b24e5^{tree} == eb88646^{tree}`, `git diff b0b24e5 eb88646` empty — so the 30/30 I reproduced is a claim about exactly the content that landed, not about a tree that no longer exists. --- ### ⚠️ The fix has not reached the box yet, and this merge is not what sends it Repeating review 2/7 here so it is the last thing on the thread rather than the second: `master` is the Supervisor's default branch, it is still `6f5e8e3` at **0.10.0**, and it still does not contain SAFETY-57. Until `master` moves, the box goes on reporting 0.10.0, goes on matching `master`, and goes on running the deadlock — with a merged PR and a green gate above it. `master` is a strict ancestor of `release/1.0`, so that ship is a **fast-forward** to `b0b24e5`, exactly as `6f5e8e3` did for 0.10.0. Nothing to resolve. Nothing was deployed, installed, or tagged by this review.
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/goodwe-addon#105