Skip to content

feat: vendor the flat emitter, fixing an add-on image that could never start - #37

Merged
cayossarian merged 2 commits into
mainfrom
feat/vendor-flat-emitter
Jul 31, 2026
Merged

feat: vendor the flat emitter, fixing an add-on image that could never start#37
cayossarian merged 2 commits into
mainfrom
feat/vendor-flat-emitter

Conversation

@cayossarian

Copy link
Copy Markdown
Member

The bug this fixes

ebus_emitter is a hard, unconditional import (app.py:36emitter_adapter/runtime.py:20), but the add-on Dockerfile installs only pip install --no-cache-dir . and the package was never a declared dependency — it is not on PyPI, and was installed editable from EBUS_EMITTER_PATH by scripts/dev-setup.sh.

So the image built fine and died at container start:

ModuleNotFoundError: No module named 'ebus_emitter'

The same applied to anyone who cloned this repo and ran uv sync without dev-setup.sh. Only developers who had run dev-setup.sh ever had a working install. Verified by blocking the module with an import hook and importing the entry point.

The change

Vendor the emitter at src/span_panel_simulator/flat_emitter, from ebus-emitter 0.2.1 (commit 5b84de8) — MIT, same copyright holders. Provenance and rationale are in the package docstring.

Upstream has permanently diverged onto the parent/child v1.0 Homie data model while this simulator continues to publish the flat schema. A dependency earns its keep by delivering upstream changes; once the fork is permanent it delivers nothing while costing path configuration, stale editable metadata, and an unsolvable distribution problem for the add-on.

It also closes a correctness hazard. clone.py seeds energy accumulators against what this code publishes. While the two lived in separate repos, each side could look locally correct while jointly inverting circuit energy — which is exactly what happened (#36), and what no single test suite could see. Both ends now sit in one repo under one test run.

The emitter's tests come with it (tests/flat_emitter/, 154 tests), including the circuit energy reference-frame regression tests. Suite is now 395 tests.

Supporting changes, each a consequence of the move

  • ebus-sdk pinned to ==0.1.5 rather than the range upstream declared, so vendoring is behaviour-neutral. That is what the emitter's lockfile resolved and what this code was tested against. Floating within <0.2 resolves 0.1.10, which drops the module-level setLevel(INFO) on the homie logger that tests/test_main_logging.py guards — the guard fired exactly as designed. The add-on installs from pyproject.toml, not the lockfile, so the declared bound is the operative control in production. Raising it should be its own PR with its own testing.
  • [tool.ruff.lint] now declares ignore = ["TC001", "TC002", "TC003"]. The existing comment already described this ignore but the key was never present — none of the simulator's own modules happened to trigger the rules, so the omission was invisible. The vendored code was authored under an identical select list plus this ignore, so this restores documented intent rather than relaxing the bar for vendored code.
  • ChargeMode exported and used to annotate the charge_mode derivation in engine.py and emitter_adapter/runtime.py. Both sites already produced only valid values; mypy could not see it while ebus_emitter was an ignore_missing_imports module and BESSConfig was therefore Any. Typing-only, no behaviour change.
  • ebus_sdk mypy overrides move here from the emitter's pyproject.
  • dev-setup.sh becomes a thin uv sync wrapper; .env.example no longer defines EBUS_EMITTER_PATH.

Packaging note

No force-include is added for the vendored wire/profiles and wire/mapping data. packages = ["src/span_panel_simulator"] already ships them, and re-declaring them would reintroduce the duplicate-path collision fixed in 1.0.12. Upstream needed force-include only because its package root was elsewhere.

Verification

  • 395 tests pass (241 simulator + 154 vendored emitter)
  • ruff check clean, mypy --strict clean across 85 source files, no new ignores beyond the two that moved with the code
  • The actual fix, end to end: installed into a clean venv exactly the way the Dockerfile does (pip install --no-cache-dir .), then confirmed the entry point imports, the vendored emitter imports, and all twelve data files are packaged
  • pre-commit hooks pass, including sync-version across all four version references

Relationship to #36

Independent — this branches from main and touches dev-setup.sh/.env.example, which #36 no longer does. They will conflict only on CHANGELOG.md and the version, both trivial. #36 carries the clone.py energy fix; this carries the dependency story.

…r start

ebus_emitter is a hard, unconditional import (app.py -> emitter_adapter/
runtime.py), but the add-on Dockerfile installs only `pip install .` and the
package was never a declared dependency — it is not on PyPI and was installed
editable from EBUS_EMITTER_PATH by scripts/dev-setup.sh. The image therefore
built successfully and died at container start with ModuleNotFoundError. The
same applied to anyone who cloned this repo and ran `uv sync` without
dev-setup.sh. Only developers who had run dev-setup.sh ever had a working
install.

Vendor the emitter at src/span_panel_simulator/flat_emitter, copied from
ebus-emitter 0.2.1 (commit 5b84de8) — MIT, same copyright holders. Upstream has
permanently diverged onto the parent/child v1.0 Homie data model while this
simulator continues to publish the flat schema, so the dependency delivered no
upstream changes while costing path configuration, stale editable metadata, and
an unsolvable distribution problem for the add-on.

It also closes a correctness hazard. clone.py seeds energy accumulators against
what this code publishes; while the two lived in separate repos each side could
look locally correct while jointly inverting circuit energy, which is exactly
what happened and what no single test suite could see. Both ends now sit in one
repo under one test run.

The emitter's tests come with it (tests/flat_emitter/, 154 tests), including the
circuit energy reference-frame regression tests. Suite is now 395 tests.

Supporting changes, each a consequence of the move:

- ebus-sdk pinned to ==0.1.5 rather than the range upstream declared, so
  vendoring is behaviour-neutral. 0.1.5 is what the emitter's lockfile resolved
  and what this code was tested against; floating within <0.2 resolves 0.1.10,
  which drops the module-level setLevel(INFO) on the homie logger that
  tests/test_main_logging.py guards. The add-on installs from pyproject rather
  than the lockfile, so the declared bound is the operative control in
  production. Raising it is a deliberate follow-up.

- [tool.ruff.lint] now declares ignore = TC001/TC002/TC003. The existing comment
  already described this ignore but the key was never present — none of the
  simulator's own modules triggered the rules, so the omission was invisible.
  The vendored code was authored under an identical select list plus this
  ignore.

- ChargeMode is exported from the vendored package and annotates the charge_mode
  derivation in engine.py and emitter_adapter/runtime.py. Both sites already
  produced only valid values; mypy could not see it while ebus_emitter was an
  ignore_missing_imports module and BESSConfig was therefore Any.

- ebus_sdk mypy overrides move here from the emitter's pyproject.

- dev-setup.sh becomes a thin uv sync wrapper; .env.example no longer defines
  EBUS_EMITTER_PATH.

No force-include is added for the vendored wire/profiles and wire/mapping data:
packages = ["src/span_panel_simulator"] already ships them, and re-declaring
them would reintroduce the duplicate-path collision fixed in 1.0.12. Verified by
installing into a clean venv the way the Dockerfile does and confirming the
entry point imports and all twelve data files are packaged.
@cayossarian
cayossarian merged commit 8883481 into main Jul 31, 2026
2 checks passed
@cayossarian
cayossarian deleted the feat/vendor-flat-emitter branch July 31, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant