What is wrong
The top level answers what kind of code (core/, strategies/, comparison/, presentation/) instead of what role it plays. Nothing in the tree says which code a real battery controller would run and which exists only to develop and evaluate it.
core/ holds both: planners/, mpc/, estimators/ (product) beside physics/plant.py, simulator/synthetic_day.py, markets/price_loader.py, accounting/ledger.py, visualization/ (harness). comparison/ and presentation/ are harness code that got their own top-level folders because they came later. archive/ (v1–v4, 13,688 lines — 54% of all Python in the repo) sits inside the import root, so every grep, IDE index and future list command has to know to skip it.
Consequences today, verifiable:
core/accounting/ledger.py:29 imports core.simulator.traces and core/planners/milp_bidding.py:1035 imports core.simulator.synthetic_day — leaves reaching into the trunk. Nothing forbids it because no boundary is stated.
- No
[project.scripts] in pyproject.toml, no __main__.py: there is no entry point, so the three runner scripts in comparison/ are the interface (see the experiment-registry issue).
- The package description advertises "EKF/MHE";
core/estimators/ holds ekf.py only.
The target shape
The one every research-simulation repo surveyed converges on (pyOpenSci, Scientific Python dev guide, Wilson et al. Good Enough Practices, and what RCWS #60 walked into): one top-level fold by role, subsystems underneath.
src/bess/ ships — the controller stack a real BESS computer would run
estimation/ planning/ control/ strategies/ config/ ports.py
devkit/ never ships — exists to develop / evaluate src
simulation/ (plant, pack, activation, synthetic day, prices, the loop, records)
accounting/ (ledger + Greek settlement — scoring)
experiments/ (the registry; today comparison/*.py)
visualization/
tests/ docs/ out/ (generated, reconstitutable)
archive/ → out of the import root
pyproject.toml → [project.scripts]
ports.py is the hardware seam written down: measurements and prices in, setpoints out — the same move as RCWS engagement/ports.py.
Decisions taken (2026-08-15)
- Ledger → devkit, at least for now. It is the referee, not a player: it reads truth traces and scores a run. The settlement math (
greek_settlement.py) is a market rule and may later be extracted as a shared pure function both sides can call — split out, not moved wholesale.
archive/ → moved out of the import root, at least for now (kept in the repo, excluded from pyproject). Tagging and deleting from main is the later option if it still costs attention.
Done when
- One-way rule held by a test:
devkit/ imports src/, never the reverse (RCWS test_src_never_imports_devkit).
src/ inventory frozen by a test, so nothing drifts in.
archive/ not on the import path; pyproject excludes it.
- Entry points declared.
- Proof: existing traces bit-identical before and after — the move changes no number.
This is phase 0 of the platform epic; every seam issue assumes it.
What is wrong
The top level answers what kind of code (
core/,strategies/,comparison/,presentation/) instead of what role it plays. Nothing in the tree says which code a real battery controller would run and which exists only to develop and evaluate it.core/holds both:planners/,mpc/,estimators/(product) besidephysics/plant.py,simulator/synthetic_day.py,markets/price_loader.py,accounting/ledger.py,visualization/(harness).comparison/andpresentation/are harness code that got their own top-level folders because they came later.archive/(v1–v4, 13,688 lines — 54% of all Python in the repo) sits inside the import root, so every grep, IDE index and futurelistcommand has to know to skip it.Consequences today, verifiable:
core/accounting/ledger.py:29importscore.simulator.tracesandcore/planners/milp_bidding.py:1035importscore.simulator.synthetic_day— leaves reaching into the trunk. Nothing forbids it because no boundary is stated.[project.scripts]inpyproject.toml, no__main__.py: there is no entry point, so the three runner scripts incomparison/are the interface (see the experiment-registry issue).core/estimators/holdsekf.pyonly.The target shape
The one every research-simulation repo surveyed converges on (pyOpenSci, Scientific Python dev guide, Wilson et al. Good Enough Practices, and what RCWS #60 walked into): one top-level fold by role, subsystems underneath.
ports.pyis the hardware seam written down: measurements and prices in, setpoints out — the same move as RCWSengagement/ports.py.Decisions taken (2026-08-15)
greek_settlement.py) is a market rule and may later be extracted as a shared pure function both sides can call — split out, not moved wholesale.archive/→ moved out of the import root, at least for now (kept in the repo, excluded frompyproject). Tagging and deleting frommainis the later option if it still costs attention.Done when
devkit/importssrc/, never the reverse (RCWStest_src_never_imports_devkit).src/inventory frozen by a test, so nothing drifts in.archive/not on the import path;pyprojectexcludes it.This is phase 0 of the platform epic; every seam issue assumes it.