Ships 0.12.0 to master (currently 0.11.0 on the reference battery).
What ships
TEL-12 (#116) — pv_entities: a list of PV power sensors for sites with several inverters. It replaces pv_entity (if set, pv_entity is ignored). The published total covers every listed sensor. If any one sensor is absent, the total is unknown, never a partial sum. This is instrumentation only: control behaviour does not change.
Merge shape — not a fast-forward. master has two ship-merge commits (78acb55, 0f14c2a) that release/1.0 lacks, and neither changes any files (git diff $(merge-base) origin/master is empty). git merge-tree --write-tree origin/master origin/release/1.0 runs clean, and the merged tree cc4415f is identical to release/1.0's tree.
Before you press Update: run predeploy_check.py --deployed-version 0.11.0 --options <file> where <file> holds the bare data.options dict, not the wrapped ha apps info export. If you pass the wrapped form, the tool prints CLEARED without having checked anything (SAFETY-60).
The review agent merges this PR. It includes no tag and no install.
Ships **0.12.0** to `master` (currently 0.11.0 on the reference battery).
**What ships**
- **TEL-12 (#116)** — `pv_entities`: a list of PV power sensors for sites with several inverters. It replaces `pv_entity` (if set, `pv_entity` is ignored). The published total covers every listed sensor. **If any one sensor is absent, the total is `unknown`**, never a partial sum. This is instrumentation only: control behaviour does not change.
- **PKG-18 (#117)** — the version bump to 0.12.0.
Slug unchanged: `goodwe_controller`.
**Merge shape** — not a fast-forward. `master` has two ship-merge commits (`78acb55`, `0f14c2a`) that `release/1.0` lacks, and neither changes any files (`git diff $(merge-base) origin/master` is empty). `git merge-tree --write-tree origin/master origin/release/1.0` runs clean, and the merged tree `cc4415f` is **identical** to `release/1.0`'s tree.
**Before you press Update:** run `predeploy_check.py --deployed-version 0.11.0 --options <file>` where `<file>` holds the **bare `data.options` dict**, not the wrapped `ha apps info` export. If you pass the wrapped form, the tool prints CLEARED without having checked anything (SAFETY-60).
The review agent merges this PR. It includes no tag and no install.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
0.11.0 shipped `pv_entity` as a single string. The reference site's owner
reported within minutes of it going live that they have several PV inverters
and could configure one - which is ordinary for a multi-string roof, not a
property of one house. `pv_entities` is the roster and the row carries the
SITE total.
⚠️ THE HARD PART IS ABSENCE, NOT THE LIST. Three configured, one `unavailable`,
and the sum of the other two is a SMALLER NUMBER INDISTINGUISHABLE FROM A REAL
ONE. Read back by OPT-10 that says "there was less PV than there was" - the "no
irradiance" conclusion the channel exists to TEST. So `pv.total()` answers
`None` the moment any configured entity is absent: the topic carries HA's own
`None`, the entity goes to `unknown`, the recorder records a GAP. The
alternative (the partial sum beside a contributor count) was implemented and
run as mutation N1b; it loses because the MQTT row is read COLD a week later by
an attribution that would believe it, while the qualifying count would live in a
second entity with its own history. The partial information is not thrown away -
the STATUS PAGE names every entity that reported, with its watts, and every one
that did not, with its reason, because that surface is read LIVE by a human who
wants the diagnostic.
⚠️ AND THE MIRROR: a member honestly reporting 0 W is SUMMED. The test is
`is None`, never falsiness - a roster that blanked the site on a zero would
report an absence on every clear evening.
THE SURFACE IS MEASURED, NOT PREFERRED. `pv_entity` could not become a list:
tools/supervisor_shape_probe.py §G shows Supervisor 2026.08.0 answering an
0.11.0 install's stored string with `Invalid list for option 'pv_entity'`, and
deepmerge's list strategy is `override` so a `[]` default does not rescue it -
the live add-on would not start. `match(.*)?` does accept both shapes and is
rejected for a different, measured reason: it validates nothing (a string, a
list and the integer 417 all pass) and renders as a plain text box. So
`pv_entities` joins `pv_entity` and REPLACES it when set, CFG-04's rule for
`inverters:`, with a status caution saying which key won - a union would
double-count the inverter the old key still names.
STILL NOT A CONTROL INPUT. tools/tel11_pv_trace.py was extended rather than
replaced and now grades five claims over three document sets. The control hash
is UNMOVED - 846a10a5b89e… on `unset` and on `one`, on both trees - and the
roster control document is byte-identical between the two trees. 2/5 is a claim
TEL-11 could not make: with 0.11.0 as the baseline, "one entity behaves exactly
as 0.11.0 does" is a real cross-tree identity over control AND telemetry.
⚠️ TEL-11's OWN `--compare` REFUSAL HAD TO CHANGE, and that is MUTATION.md habit
24: a discriminator defined as "the baseline lacks this feature" dies on the
release that ships the feature, and dies as a false refusal.
Twelve mutations, each on a fresh purged `cp -a`, no survivors. N5 is +0 B
(`sum` -> `max`, the same three characters) and was run on a fresh copy with
`cmp` confirming the edit on disk. The fixture is three distinct NON-ZERO
contributions in two units with absence driven at EVERY position - a member
contributing 0 W could go absent without moving the sum of the rest, which is
habit 23 with a coinciding value in the arithmetic rather than in the plant.
31/31 in the CI image. Not deployable as merged: config.yaml stays at 0.11.0,
so predeploy_check.py reports a NO-OP until PKG cuts a version. A CHANGELOG
entry is wanted and deliberately not written - 11b forbids `## Unreleased` and
the bump is PKG's call; DECISIONS/TEL-12-several-pv-inverters.md is the
material it needs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
pv_entity becomes a roster: pv_entities beside it, replacing it, with the SITE total over the roster. One absent member makes the TOTAL absent - never the partial sum of the ones that answered, because a smaller number indistinguishable from a real one reads a week later as 'there was less sun than there was', which is the conclusion OPT-10 exists to test rather than to manufacture. A member honestly reporting 0 W is still summed.
Reviewed on PR 116. Re-run independently: supervisor_shape_probe.py SSG (exit 0, transcript reproduces byte for byte), the owner's real 75-key options blob through Supervisor 2026.08.0's own AppOptions (ACCEPTED, zero drift on any stored value, pv_entities defaulted to []), tel11_pv_trace.py --compare across both trees (every hash reproduces, control 846a10a5b89e unmoved on unset and one), the rejected-design mutant (trace REFUSED 5/5 with the partial sums named), the planted leak N8 (52/26/10 red cells), and eight mutations on fresh purged copies including N5 sum->max at +0 bytes - no survivors. Habit 24's central claim measured: TEL-11's own --compare refuses on every honest run now that release/1.0 is 0.11.0.
Green traced to this PR's own run: 829/1954, attempt 1, event pull_request, path checks.yml@refs/pull/116/head, payload naming PR 116 with base release/1.0 @ cccb2fa, 31 suites PASS in 7m53s - a real execution, not a laundered skip.
Not deployable as merged: config.yaml stays 0.11.0 and the CHANGELOG entry is PKG's call.
Release review for 0.12.0: approved, merging. Checked from a fresh clone and the Gitea API. This is the release decision only. TEL-12 (#116) and the cut (#117) were reviewed on their own PRs.
1. Identity.release/1.0:goodwe_controller/config.yaml still has slug: goodwe_controller, the same as master, so this updates the existing add-on and does not create a second one. version changes from "0.11.0" to "0.12.0". In config.yaml the only other changes add the pv_entities: [] option and its - str schema entry. The existing keys are untouched.
2. Merge shape. This is not a fast-forward: master (78acb55) is not an ancestor of a45ed06. The merge base is cccb2fa, and git diff cccb2fa origin/master is empty, so master's two extra commits (78acb55, 0f14c2a) are merges that change no files. git merge-tree --write-tree origin/master origin/release/1.0 exits 0 and gives cc4415f355e3…, which is exactly release/1.0^{tree}. The merge adds nothing beyond what was reviewed.
3. The green belongs to this PR (CI-10). All three statuses on a45ed06 point to run 831 / job 1956. None of them come from #117. From the run and the job log:
the run is event: pull_request, path: checks.yml@refs/pull/118/head, run_attempt: 1, conclusion: success
the log shows PRNUM: 118 and BASEREF: master, with base 78acb558… (still master's head) and head refs/pull/118/head a45ed06…
GRADED: refs/pull/118/head merged into master, result 46738d1, tree cc4415f355e3…, which is the same tree as check 2. The job graded the exact content that merging produces
31 test file(s) to run: 31 PASS, 0 FAIL, Job succeeded. The repo has 31 test_*.py files
No old runs were rerun.
4. The "Before you press Update" line is present and correct. predeploy_check.py takes --deployed-version and --options, and passes the parsed JSON directly to validate_startup_config. A wrapped export would be validated as the wrong dict (SAFETY-60, still unfixed). So running it against the bare data.options dict with --deployed-version 0.11.0 is the right instruction.
Not followed on purpose:docs/RELEASE.md §1.4 (who merges) and §1.5 (the chained ha store reload && ha addons update local_goodwe_controller has no predeploy or go step, and it names the wrong slug; the live install is 68285ac7_goodwe_controller). These are known docs defects being carded. This merge does not create a tag or run an install. Merging makes 0.12.0 offerable. Deploying still needs the predeploy run above and the owner's go.
**Release review for 0.12.0: approved, merging.** Checked from a fresh clone and the Gitea API. This is the release decision only. TEL-12 (#116) and the cut (#117) were reviewed on their own PRs.
**1. Identity.** `release/1.0:goodwe_controller/config.yaml` still has `slug: goodwe_controller`, the same as `master`, so this updates the existing add-on and does not create a second one. `version` changes from `"0.11.0"` to `"0.12.0"`. In `config.yaml` the only other changes add the `pv_entities: []` option and its `- str` schema entry. The existing keys are untouched.
**2. Merge shape.** This is not a fast-forward: `master` (`78acb55`) is not an ancestor of `a45ed06`. The merge base is `cccb2fa`, and `git diff cccb2fa origin/master` is empty, so master's two extra commits (`78acb55`, `0f14c2a`) are merges that change no files. `git merge-tree --write-tree origin/master origin/release/1.0` exits 0 and gives `cc4415f355e3…`, which is exactly `release/1.0^{tree}`. The merge adds nothing beyond what was reviewed.
**3. The green belongs to this PR (CI-10).** All three statuses on `a45ed06` point to run 831 / job 1956. None of them come from #117. From the run and the job log:
- the run is `event: pull_request`, `path: checks.yml@refs/pull/118/head`, `run_attempt: 1`, `conclusion: success`
- the log shows `PRNUM: 118` and `BASEREF: master`, with base `78acb558…` (still master's head) and head `refs/pull/118/head a45ed06…`
- `GRADED: refs/pull/118/head merged into master`, result `46738d1`, tree `cc4415f355e3…`, which is the same tree as check 2. The job graded the exact content that merging produces
- `31 test file(s) to run`: 31 PASS, 0 FAIL, `Job succeeded`. The repo has 31 `test_*.py` files
No old runs were rerun.
**4. The "Before you press Update" line** is present and correct. `predeploy_check.py` takes `--deployed-version` and `--options`, and passes the parsed JSON directly to `validate_startup_config`. A wrapped export would be validated as the wrong dict (SAFETY-60, still unfixed). So running it against the bare `data.options` dict with `--deployed-version 0.11.0` is the right instruction.
**Not followed on purpose:** `docs/RELEASE.md` §1.4 (who merges) and §1.5 (the chained `ha store reload && ha addons update local_goodwe_controller` has no predeploy or go step, and it names the wrong slug; the live install is `68285ac7_goodwe_controller`). These are known docs defects being carded. This merge does not create a tag or run an install. Merging makes 0.12.0 offerable. Deploying still needs the predeploy run above and the owner's go.
admin
merged commit 3ce18708b6 into master2026-09-24 23:22:35 +02:00
Merged at a45ed06, pinned with head_commit_id. Checked with git ls-remote against the remote: refs/heads/master is now 3ce18708b6d71c7221dcba21c93d10f7cbb3f28b, a merge of 78acb55 and a45ed06. Its tree is cc4415f355e3…, the same tree run 831 graded, and config.yaml there reads version: "0.12.0" and slug: goodwe_controller. No tag was created and nothing was installed. Deploying still needs the predeploy run on the bare data.options dict and the owner's go.
Merged at `a45ed06`, pinned with `head_commit_id`. Checked with `git ls-remote` against the remote: `refs/heads/master` is now `3ce18708b6d71c7221dcba21c93d10f7cbb3f28b`, a merge of `78acb55` and `a45ed06`. Its tree is `cc4415f355e3…`, the same tree run 831 graded, and `config.yaml` there reads `version: "0.12.0"` and `slug: goodwe_controller`. No tag was created and nothing was installed. Deploying still needs the predeploy run on the bare `data.options` dict and the owner's go.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Ships 0.12.0 to
master(currently 0.11.0 on the reference battery).What ships
pv_entities: a list of PV power sensors for sites with several inverters. It replacespv_entity(if set,pv_entityis ignored). The published total covers every listed sensor. If any one sensor is absent, the total isunknown, never a partial sum. This is instrumentation only: control behaviour does not change.Slug unchanged:
goodwe_controller.Merge shape — not a fast-forward.
masterhas two ship-merge commits (78acb55,0f14c2a) thatrelease/1.0lacks, and neither changes any files (git diff $(merge-base) origin/masteris empty).git merge-tree --write-tree origin/master origin/release/1.0runs clean, and the merged treecc4415fis identical torelease/1.0's tree.Before you press Update: run
predeploy_check.py --deployed-version 0.11.0 --options <file>where<file>holds the baredata.optionsdict, not the wrappedha apps infoexport. If you pass the wrapped form, the tool prints CLEARED without having checked anything (SAFETY-60).The review agent merges this PR. It includes no tag and no install.
🤖 Generated with Claude Code
https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
Release review for 0.12.0: approved, merging. Checked from a fresh clone and the Gitea API. This is the release decision only. TEL-12 (#116) and the cut (#117) were reviewed on their own PRs.
1. Identity.
release/1.0:goodwe_controller/config.yamlstill hasslug: goodwe_controller, the same asmaster, so this updates the existing add-on and does not create a second one.versionchanges from"0.11.0"to"0.12.0". Inconfig.yamlthe only other changes add thepv_entities: []option and its- strschema entry. The existing keys are untouched.2. Merge shape. This is not a fast-forward:
master(78acb55) is not an ancestor ofa45ed06. The merge base iscccb2fa, andgit diff cccb2fa origin/masteris empty, so master's two extra commits (78acb55,0f14c2a) are merges that change no files.git merge-tree --write-tree origin/master origin/release/1.0exits 0 and givescc4415f355e3…, which is exactlyrelease/1.0^{tree}. The merge adds nothing beyond what was reviewed.3. The green belongs to this PR (CI-10). All three statuses on
a45ed06point to run 831 / job 1956. None of them come from #117. From the run and the job log:event: pull_request,path: checks.yml@refs/pull/118/head,run_attempt: 1,conclusion: successPRNUM: 118andBASEREF: master, with base78acb558…(still master's head) and headrefs/pull/118/head a45ed06…GRADED: refs/pull/118/head merged into master, result46738d1, treecc4415f355e3…, which is the same tree as check 2. The job graded the exact content that merging produces31 test file(s) to run: 31 PASS, 0 FAIL,Job succeeded. The repo has 31test_*.pyfilesNo old runs were rerun.
4. The "Before you press Update" line is present and correct.
predeploy_check.pytakes--deployed-versionand--options, and passes the parsed JSON directly tovalidate_startup_config. A wrapped export would be validated as the wrong dict (SAFETY-60, still unfixed). So running it against the baredata.optionsdict with--deployed-version 0.11.0is the right instruction.Not followed on purpose:
docs/RELEASE.md§1.4 (who merges) and §1.5 (the chainedha store reload && ha addons update local_goodwe_controllerhas no predeploy or go step, and it names the wrong slug; the live install is68285ac7_goodwe_controller). These are known docs defects being carded. This merge does not create a tag or run an install. Merging makes 0.12.0 offerable. Deploying still needs the predeploy run above and the owner's go.Merged at
a45ed06, pinned withhead_commit_id. Checked withgit ls-remoteagainst the remote:refs/heads/masteris now3ce18708b6d71c7221dcba21c93d10f7cbb3f28b, a merge of78acb55anda45ed06. Its tree iscc4415f355e3…, the same tree run 831 graded, andconfig.yamlthere readsversion: "0.12.0"andslug: goodwe_controller. No tag was created and nothing was installed. Deploying still needs the predeploy run on the baredata.optionsdict and the owner's go.