Skip to content

Commit 1ba4836

Browse files
authored
Merge pull request #2 from miklosbagi/invforge-import2
InvForge: multi-vendor inverter/BESS Modbus TCP emulator
2 parents 5d1befc + 8b1eeac commit 1ba4836

57 files changed

Lines changed: 3080 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
---
2+
name: invforge-review
3+
description: Mandatory pre-push review for InvForge (Python). Runs ruff/mypy/pytest gates and reviews changed files against docs/coding-standards.md. Must be run and must pass before any `git push` in this repo.
4+
---
5+
6+
# InvForge pre-push review
7+
8+
This is a **mandatory gate**, not an optional lint pass. No commit in this
9+
repo gets pushed without this skill having been run against it and having
10+
reported a clean result. If you are about to `git push` in this repo and
11+
haven't run this skill against the commits being pushed, run it first.
12+
13+
## 1. Scope the review
14+
15+
Determine what's changed and being pushed:
16+
17+
```sh
18+
git status --short
19+
git diff --stat $(git merge-base HEAD origin/main 2>/dev/null || echo HEAD)..HEAD
20+
```
21+
22+
If there's no `origin/main` yet (new repo, nothing pushed), review
23+
everything staged/committed so far instead.
24+
25+
## 2. Mechanical gates — all must pass with zero errors/warnings
26+
27+
Run from the repo root, using the project venv:
28+
29+
```sh
30+
.venv/bin/ruff format --check invforge tests
31+
.venv/bin/ruff check invforge tests
32+
.venv/bin/mypy
33+
.venv/bin/python -m pytest tests/unit -q
34+
```
35+
36+
`tests/unit/` is fast and network-free — always run it. If the change
37+
under review touches anything in `invforge/core/connectivity.py`,
38+
`invforge/core/server.py`, `invforge/core/control_api.py`, a scenario
39+
fixture, or the Dockerfile/compose file, also run the integration suite
40+
(needs Docker):
41+
42+
```sh
43+
scripts/integration-test.sh
44+
```
45+
46+
Any failure here is an automatic FAIL — fix it (or, for `ruff format`,
47+
just run `ruff format invforge tests` and re-check) before doing the
48+
semantic review below. Don't hand-wave a mypy or ruff error as
49+
"pre-existing" without confirming it actually predates the change under
50+
review — the whole codebase is expected to be clean at all times, see
51+
`docs/coding-standards.md`.
52+
53+
## 3. Semantic review
54+
55+
Read `docs/coding-standards.md` in full, then walk every changed file
56+
against it. In particular:
57+
58+
- **Correctness against real fixtures**, not just synthetic ones — does
59+
the change still work against `scenarios/recorded/*.yaml`, not only
60+
invented scenarios?
61+
- **Boundary conditions**: empty scenario, register at a unit's block
62+
edge, malformed/partial YAML, an HTTP request with a missing/wrong-typed
63+
field.
64+
- **Resource cleanup** on every exit path — threads, sockets, the
65+
ticker's stop_event.
66+
- **Type-hint accuracy** — does the annotation actually match runtime
67+
behavior, or did mypy only pass because of an `Any` escape hatch
68+
(`# type: ignore`, unannotated `object`, etc.) that hides a real gap?
69+
Every `# type: ignore` in this codebase should have a comment
70+
explaining why (see `core/server.py`'s pymodbus-subclassing comment as
71+
the reference example) — a bare, unexplained one is itself a finding.
72+
- **Test coverage** for the behavior actually changed — a new code path
73+
with no corresponding test is a finding, not just a suggestion.
74+
- **Consistency with `invforge/core/`'s conventions** — did a
75+
vendor-specific quirk leak into `core/` instead of staying inside its
76+
`invforge/profiles/<vendor>/`? That's an architecture violation, not a
77+
style nit.
78+
- **New dependency scrutiny** — anything added to `requirements.txt`
79+
that the stdlib or an existing dependency already covers is a finding.
80+
81+
## 4. Report
82+
83+
Use the `ReportFindings` tool with the verified findings, most severe
84+
first. If nothing survives review, call it with an empty findings list —
85+
don't skip the call.
86+
87+
## 5. Verdict
88+
89+
State explicitly, in plain text after the findings:
90+
91+
- **PASS — safe to push** if all mechanical gates passed and no
92+
CONFIRMED correctness finding remains, or
93+
- **FAIL — do not push** otherwise, naming exactly what has to change
94+
first.
95+
96+
Do not push on a FAIL verdict, even if the user seems to be in a hurry —
97+
say so and let them explicitly override if they choose to.

‎.dockerignore‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.venv/
2+
__pycache__/
3+
*.pyc
4+
.mypy_cache/
5+
.ruff_cache/
6+
.pytest_cache/
7+
.git/
8+
tests/

‎.github/workflows/ci.yml‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: CI
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
unit:
7+
runs-on: ubuntu-latest
8+
permissions:
9+
contents: read
10+
code-quality: write
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-python@v5
14+
with:
15+
python-version: "3.11"
16+
- run: pip install -r requirements-dev.txt
17+
- run: ruff format --check invforge tests
18+
- run: ruff check invforge tests
19+
- run: mypy
20+
- run: python -m pytest tests/unit -q --cov=invforge --cov-report=xml
21+
- name: Upload coverage report
22+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
23+
uses: actions/upload-code-coverage@v1
24+
with:
25+
file: coverage.xml
26+
language: Python
27+
# Soft-fail until "Code Quality" is enabled in this repo's
28+
# Settings -> Code security page (no API for that toggle as of
29+
# this writing, so it can't be flipped from here) -- without
30+
# this, every CI run hard-fails on a 404 from the upload
31+
# endpoint before that's turned on.
32+
fail-on-error: false
33+
34+
integration:
35+
runs-on: ubuntu-latest
36+
needs: unit
37+
steps:
38+
- uses: actions/checkout@v4
39+
- uses: actions/setup-python@v5
40+
with:
41+
python-version: "3.11"
42+
- run: pip install -r requirements-dev.txt
43+
- run: docker compose up -d --build
44+
- run: scripts/wait-healthy.sh invforge
45+
- run: python -m pytest tests/integration -q
46+
env:
47+
INVFORGE_MODBUS_PORT: "5020"
48+
INVFORGE_CONTROL_PORT: "8080"
49+
- if: failure()
50+
run: docker compose logs
51+
- if: always()
52+
run: docker compose down

‎.github/workflows/codeql.yml‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: CodeQL
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
pull_request:
7+
branches: ["main"]
8+
schedule:
9+
- cron: "22 6 * * 3"
10+
11+
jobs:
12+
analyze:
13+
name: Analyze (python)
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
security-events: write
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- uses: github/codeql-action/init@v3
23+
with:
24+
languages: python
25+
queries: security-and-quality
26+
27+
- uses: github/codeql-action/analyze@v3
28+
with:
29+
category: "/language:python"

‎.gitignore‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
__pycache__/
2+
*.pyc
3+
.venv/
4+
*.egg-info/
5+
.coverage
6+
coverage.xml

‎CLAUDE.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# InvForge — agent instructions
2+
3+
Before any `git push` in this repo, run the `invforge-review` skill
4+
against the commits being pushed and get a PASS verdict. This is a hard
5+
requirement (see `.claude/skills/invforge-review/SKILL.md`), not a
6+
suggestion — do not push on a FAIL verdict without the user explicitly
7+
overriding it.
8+
9+
See `docs/coding-standards.md` for the rules that review enforces.

‎Dockerfile‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM python:3.12-slim
2+
3+
WORKDIR /app
4+
5+
RUN apt-get update \
6+
&& apt-get install -y --no-install-recommends curl \
7+
&& rm -rf /var/lib/apt/lists/*
8+
9+
COPY requirements.txt .
10+
RUN pip install --no-cache-dir -r requirements.txt
11+
12+
COPY invforge ./invforge
13+
14+
EXPOSE 502 8080
15+
16+
HEALTHCHECK --interval=2s --timeout=2s --start-period=5s --retries=5 \
17+
CMD curl -sf http://127.0.0.1:8080/health || exit 1
18+
19+
ENTRYPOINT ["python", "-m", "invforge"]
20+
CMD ["--vendor", "sigenergy", "--firmware", "V100R001C21SPC116", "--modbus-port", "502", "--control-port", "8080"]

‎LICENSE‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 InvForge contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)