CI-01: this repository's first automated check #13

Merged
admin merged 1 commits from CI-01 into release/1.0 2026-08-30 16:00:42 +02:00
Owner

Part of CI-01. The infra-repo half is admin/goodwe#30, which carries the full diagnosis.

This repository had no CI at all

Before this, admin/goodwe-addon had no .gitea/workflows/ directory. Eight runnable test files — test_loop.py alone drives the real run_control() for 93 s of wall clock — and not one had ever been run by a machine. Every merge was a human reading a diff. The product owner's requirement that a green CI gate a merge could not be met on a repository with no CI to be green.

One job, unit, no paths: filter, so it reports on every PR and can therefore be a required status check: Gitea leaves a required context it never receives pending forever, which is the failure mode on the other side of this ticket. Concurrency is scoped per PR from the start — a bare group name is what let the infra repo's run 521 be superseded while still pending, so its four jobs concluded cancelled with started_at at the Unix epoch and its PR merged on no evidence at all.

Green, first run

python 3.14.7 aiohttp 3.13.5
8 test file(s) to run
PASS  test_arbiter.py  (0s)
PASS  test_control.py  (0s)
PASS  test_freeze.py  (33s)
PASS  test_loop.py  (93s)
PASS  test_maintenance.py  (0s)
PASS  test_p1.py  (8s)
PASS  test_validate.py  (5s)
PASS  test_watchdog.py  (8s)

admin/goodwe-addon run 1 (id 526), 2 m 39 s.

The image is the shipped dependency set

.gitea/ci/Dockerfile is alpine:3 plus py3-aiohttp and py3-paho-mqtt from apk, matching goodwe_controller/Dockerfile exactly. pip would install glibc wheels of a library the shipped image gets from Alpine — there are no musl wheels for aiohttp, which is why the real Dockerfile uses apk in the first place. The runner itself cannot host these tests: bare python3, no aiohttp, no pip (checked on the box), and mutating a shared long-lived runner with apk add is invisible to anyone reading this repo.

Not BUILD_FROM's base: that is per-architecture and not resolvable outside a Supervisor build. alpine:3 is what those images are, minus the s6 supervision the tests do not use, and it is already cached on the runner's host.

Two things measured while building this, recorded rather than relearned

The checkout must sit two levels below the mount root. test_freeze.py's find_hwsim() reads HERE.parents[1] / "goodwe" / "sim", looking for the infra repo as a sibling. Checked out flat at the mount root, parents[1] does not exist and the file dies on IndexError: 1 before its first assertion. Measured — the flat layout was tried first and did exactly that.

test_freeze.py's PASS is a weaker claim than it looks. Its third section wants sim/hwsim.py from admin/goodwe, which is private, and this workflow's GITHUB_TOKEN is minted for this repository only. So hwsim is absent, find_hwsim() returns None, that section does not run — and the file prints nothing to say so before reporting all checks passed. Absence read as coverage, in the file TEST-06 is currently rewriting. Named in the workflow header; routed to that reviewer rather than fixed here.

Branch protection

Before-state, GET /api/v1/repos/admin/goodwe-addon/branch_protections at 2026-08-30T13:47:25Z:

/branch_protections               -> HTTP 200  []
/branch_protections/release%2F1.0 -> HTTP 404  {"message":"not found"}

No rules of any kind. Applied: enable_status_check: true, status_check_contexts: ["checks / unit (pull_request)"] — taken from a real run's commit statuses, not guessed — block_admin_merge_override: true, enable_push: false, enable_force_push: false. To reverse: DELETE /api/v1/repos/admin/goodwe-addon/branch_protections/release%2F1.0.

Adoption cost, stated plainly: a branch cut before this workflow existed produces no run and therefore no context, so it cannot merge until it takes one merge from release/1.0. That is a one-time cost per open PR, not a defect, but it is invisible from the PR page — the checks list is simply empty — so it is written down here.


⚠️ THE GATE AS SHIPPED HAS A KNOWN HOLE. Do not read this card as "the gate is trustworthy."

Filed as CI-02. Two ways checks / unit has already misreported, both observed on admin/goodwe-addon#14, and both are this card's own failure shape living inside the gate meant to remove it:

1. Green on content that cannot merge. The checkout step here is
git fetch -q --depth 1 origin "$GITHUB_REF" || git fetch -q --depth 1 origin "$GITHUB_SHA".
For a pull_request event $GITHUB_REF is the merge ref, which does not exist when the PR is conflicted — so the first fetch fails and the fallback silently tests the bare head instead. PR #14 reported checks / unit -> success while mergeable: false. A green on the merge result and a green on the head alone are indistinguishable in the output.

⚠️ Do not "fix" this by deleting the ||. The fallback is deliberate: $GITHUB_REF is not always fetchable, and removing it moves the failure rather than closing it. The cheap half is to print which ref actually resolved, so a reader can tell what was tested; the honest half is to refuse to report success at all when the merge ref is missing, because a pass on unmergeable content answers a question nobody asked.

2. A green that belongs to a different commit. A run is created only when the PR head tree contains .gitea/workflows/checks.yml — measured four times, not inferred:

PR head carries checks.yml run created?
goodwe#29 @ ec45b7b8 404 none; 0 statuses
goodwe-addon#14 @ 0942228d 404 none; newest run stayed on the pre-rebase commit
goodwe-addon#14 @ 4b521b2d 200 run 7 created immediately
goodwe-addon#12 after sync 200 run created, context success

So "no run was created" is explained by the head not carrying the workflow — not by the paths: filter, not by the concurrency group, and not by Gitea refusing merge commits. Anyone fixing CI-02 should rule those in or out against this table before changing them, because the wrong fix re-opens the hole somewhere else.

⚠️ One thing this table does NOT explain, and it matters most. At 0942228d the context read pending while no run existed for that sha. A pending status with nothing behind it is precisely "a reading that observed nothing", and it is the state a reviewer is most likely to mistake for "still running". The leading candidate is that branch protection manufactures the pending when a required context has never reported — which would mean the gate's own enforcement is generating the misleading signal. That is a hypothesis, not a finding; it wants reproducing before anything is changed.

Note the Actions log endpoints 404 on this Gitea, so which half of the || fired cannot be read from logs. Expect to reproduce rather than inspect — and construct the input rather than probing the gate by attempting to push past it.

Part of CI-01. The infra-repo half is `admin/goodwe#30`, which carries the full diagnosis. ## This repository had no CI at all Before this, `admin/goodwe-addon` had no `.gitea/workflows/` directory. Eight runnable test files — `test_loop.py` alone drives the real `run_control()` for 93 s of wall clock — and not one had ever been run by a machine. Every merge was a human reading a diff. The product owner's requirement that a green CI gate a merge could not be met on a repository with no CI to be green. One job, `unit`, **no `paths:` filter**, so it reports on every PR and can therefore be a required status check: Gitea leaves a required context it never receives pending forever, which is the failure mode on the other side of this ticket. Concurrency is scoped per PR from the start — a bare group name is what let the infra repo's run 521 be superseded while still pending, so its four jobs concluded `cancelled` with `started_at` at the Unix epoch and its PR merged on no evidence at all. ## Green, first run ``` python 3.14.7 aiohttp 3.13.5 8 test file(s) to run PASS test_arbiter.py (0s) PASS test_control.py (0s) PASS test_freeze.py (33s) PASS test_loop.py (93s) PASS test_maintenance.py (0s) PASS test_p1.py (8s) PASS test_validate.py (5s) PASS test_watchdog.py (8s) ``` `admin/goodwe-addon` run 1 (id 526), 2 m 39 s. ## The image is the shipped dependency set `.gitea/ci/Dockerfile` is `alpine:3` plus `py3-aiohttp` and `py3-paho-mqtt` from apk, matching `goodwe_controller/Dockerfile` exactly. pip would install glibc wheels of a library the shipped image gets from Alpine — there are no musl wheels for aiohttp, which is why the real Dockerfile uses apk in the first place. The runner itself cannot host these tests: bare python3, no aiohttp, no pip (checked on the box), and mutating a shared long-lived runner with `apk add` is invisible to anyone reading this repo. Not `BUILD_FROM`'s base: that is per-architecture and not resolvable outside a Supervisor build. `alpine:3` is what those images are, minus the s6 supervision the tests do not use, and it is already cached on the runner's host. ## Two things measured while building this, recorded rather than relearned **The checkout must sit two levels below the mount root.** `test_freeze.py`'s `find_hwsim()` reads `HERE.parents[1] / "goodwe" / "sim"`, looking for the infra repo as a sibling. Checked out flat at the mount root, `parents[1]` does not exist and the file dies on `IndexError: 1` before its first assertion. Measured — the flat layout was tried first and did exactly that. **`test_freeze.py`'s PASS is a weaker claim than it looks.** Its third section wants `sim/hwsim.py` from `admin/goodwe`, which is **private**, and this workflow's `GITHUB_TOKEN` is minted for this repository only. So hwsim is absent, `find_hwsim()` returns `None`, that section does not run — and the file prints nothing to say so before reporting `all checks passed`. Absence read as coverage, in the file TEST-06 is currently rewriting. Named in the workflow header; routed to that reviewer rather than fixed here. ## Branch protection Before-state, `GET /api/v1/repos/admin/goodwe-addon/branch_protections` at **2026-08-30T13:47:25Z**: ``` /branch_protections -> HTTP 200 [] /branch_protections/release%2F1.0 -> HTTP 404 {"message":"not found"} ``` **No rules of any kind.** Applied: `enable_status_check: true`, `status_check_contexts: ["checks / unit (pull_request)"]` — taken from a real run's commit statuses, not guessed — `block_admin_merge_override: true`, `enable_push: false`, `enable_force_push: false`. To reverse: `DELETE /api/v1/repos/admin/goodwe-addon/branch_protections/release%2F1.0`. **Adoption cost, stated plainly:** a branch cut before this workflow existed produces no run and therefore no context, so it cannot merge until it takes one merge from `release/1.0`. That is a one-time cost per open PR, not a defect, but it is invisible from the PR page — the checks list is simply empty — so it is written down here. --- ## ⚠️ THE GATE AS SHIPPED HAS A KNOWN HOLE. Do not read this card as "the gate is trustworthy." Filed as **CI-02**. Two ways `checks / unit` has already misreported, both observed on `admin/goodwe-addon#14`, and both are this card's own failure shape living inside the gate meant to remove it: **1. Green on content that cannot merge.** The checkout step here is `git fetch -q --depth 1 origin "$GITHUB_REF" || git fetch -q --depth 1 origin "$GITHUB_SHA"`. For a `pull_request` event `$GITHUB_REF` is the **merge** ref, which does not exist when the PR is conflicted — so the first fetch fails and the fallback silently tests the **bare head** instead. PR #14 reported `checks / unit -> success` while `mergeable: false`. A green on the merge result and a green on the head alone are indistinguishable in the output. ⚠️ **Do not "fix" this by deleting the `||`.** The fallback is deliberate: `$GITHUB_REF` is not always fetchable, and removing it moves the failure rather than closing it. The cheap half is to **print which ref actually resolved**, so a reader can tell what was tested; the honest half is to refuse to report success at all when the merge ref is missing, because a pass on unmergeable content answers a question nobody asked. **2. A green that belongs to a different commit.** A run is created **only when the PR head tree contains `.gitea/workflows/checks.yml`** — measured four times, not inferred: | PR | head carries `checks.yml` | run created? | |---|---|---| | `goodwe#29` @ `ec45b7b8` | **404** | none; 0 statuses | | `goodwe-addon#14` @ `0942228d` | **404** | none; newest run stayed on the pre-rebase commit | | `goodwe-addon#14` @ `4b521b2d` | **200** | run 7 created immediately | | `goodwe-addon#12` after sync | **200** | run created, context `success` | So "no run was created" is explained by the head not carrying the workflow — **not** by the `paths:` filter, **not** by the concurrency group, and **not** by Gitea refusing merge commits. Anyone fixing CI-02 should rule those in or out against this table before changing them, because the wrong fix re-opens the hole somewhere else. ⚠️ **One thing this table does NOT explain, and it matters most.** At `0942228d` the context read `pending` while **no run existed for that sha**. A pending status with nothing behind it is precisely "a reading that observed nothing", and it is the state a reviewer is most likely to mistake for "still running". The leading candidate is that branch protection manufactures the `pending` when a required context has never reported — which would mean **the gate's own enforcement is generating the misleading signal**. That is a hypothesis, not a finding; it wants reproducing before anything is changed. Note the Actions **log endpoints 404 on this Gitea**, so which half of the `||` fired cannot be read from logs. Expect to reproduce rather than inspect — and construct the input rather than probing the gate by attempting to push past it.
admin added 1 commit 2026-08-30 15:46:48 +02:00
CI-01: this repository's first automated check
checks / unit (pull_request) Successful in 2m39s
9c1a23328a
Before this commit `admin/goodwe-addon` had no `.gitea/workflows/` directory at
all. Eight runnable test files - `test_loop.py` alone drives the real
`run_control()` for 93 s of wall clock - and not one of them had ever been run
by a machine. Every merge here was a human reading a diff, and the product
owner's requirement that a green CI gate a merge could not be met on a
repository with no CI to be green.

One job, `unit`, with no `paths:` filter, so it reports on every PR and can
therefore be a REQUIRED status check: Gitea leaves a required context it never
receives pending forever, which is the failure mode on the other side of this
ticket. Concurrency is scoped per PR from the start rather than to a bare group
name - the bare name is what let the infra repo's run 521 be superseded while
still pending, so its four jobs concluded `cancelled` with `started_at` at the
Unix epoch and its PR merged on no evidence at all.

The tests run in `.gitea/ci/Dockerfile`: `alpine:3` plus `py3-aiohttp` and
`py3-paho-mqtt` from apk, matching goodwe_controller/Dockerfile exactly, because
pip would install glibc wheels of a library the shipped image gets from Alpine.
The runner itself cannot host them - bare python3, no aiohttp, no pip - and
mutating a shared long-lived runner with `apk add` is invisible to anyone
reading this repo.

Two things measured while building it and recorded in the file rather than
learned again: the checkout must sit two levels below the mount root or
test_freeze.py's `find_hwsim()` dies on `IndexError: 1` before its first
assertion, and test_freeze's hwsim section is silently absent here because
admin/goodwe is private - so its PASS is a weaker claim than it looks, and the
file says so.

Verified green on release/1.0 content before this workflow was written: 8 of 8
files pass, ~4 minutes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VXjA3LWccN1rJsk3okyPC
admin merged commit 5e966ca59f into release/1.0 2026-08-30 16:00:42 +02:00
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#13