psse-utils 2026.6.0b1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ name: Test & Coverage
10
+ runs-on: ubuntu-latest
11
+ env:
12
+ # platformdirs' site_data_dir defaults to /usr/local/share (root-only on
13
+ # the GHA runner). Redirect it to a writable path so Model.__init__'s
14
+ # pickle cache can be written during the end-to-end CLI tests.
15
+ XDG_DATA_DIRS: /tmp/psse-site-data
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python 3.11
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: '3.11'
23
+
24
+ # Installs flowgate-key-facilities (editable) plus its runtime deps from PyPI,
25
+ # including psse-model-util. --pre is needed while psse-model-util is a
26
+ # pre-release. pytest config adds the repo root to sys.path.
27
+ - name: Install package + test deps
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ python -m pip install --pre -e .
31
+ python -m pip install pytest pytest-cov
32
+
33
+ - name: Run pytest (enforces the 95% coverage gate)
34
+ run: pytest
@@ -0,0 +1,66 @@
1
+ name: Publish to PyPI
2
+
3
+ # Trusted Publishing (OIDC) — no API tokens. Triggered when a GitHub Release is
4
+ # published, or manually via "Run workflow". The TestPyPI job runs automatically;
5
+ # the PyPI job is gated by the `pypi` environment (add a required reviewer in
6
+ # repo Settings → Environments).
7
+ #
8
+ # Note: a Release created by another workflow's GITHUB_TOKEN cannot trigger this
9
+ # one, so use the manual workflow_dispatch (or a human-created Release).
10
+ on:
11
+ release:
12
+ types: [published]
13
+ workflow_dispatch:
14
+
15
+ jobs:
16
+ build:
17
+ name: Build sdist + wheel
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.11"
24
+ - name: Build
25
+ run: |
26
+ python -m pip install --upgrade build
27
+ python -m build
28
+ - uses: actions/upload-artifact@v4
29
+ with:
30
+ name: dist
31
+ path: dist/
32
+
33
+ publish-testpypi:
34
+ name: Publish to TestPyPI
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ environment:
38
+ name: testpypi
39
+ url: https://test.pypi.org/p/psse-utils
40
+ permissions:
41
+ id-token: write
42
+ steps:
43
+ - uses: actions/download-artifact@v4
44
+ with:
45
+ name: dist
46
+ path: dist/
47
+ - uses: pypa/gh-action-pypi-publish@release/v1
48
+ with:
49
+ repository-url: https://test.pypi.org/legacy/
50
+ skip-existing: true
51
+
52
+ publish-pypi:
53
+ name: Publish to PyPI
54
+ needs: publish-testpypi
55
+ runs-on: ubuntu-latest
56
+ environment:
57
+ name: pypi
58
+ url: https://pypi.org/p/psse-utils
59
+ permissions:
60
+ id-token: write
61
+ steps:
62
+ - uses: actions/download-artifact@v4
63
+ with:
64
+ name: dist
65
+ path: dist/
66
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ _smoke_out/
5
+ dist/
6
+ *.egg-info/
7
+ .coverage
8
+ .coverage.*
9
+ .pytest_cache/
10
+ .pdm-python
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cadvena
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,0 +1,81 @@
1
+ Metadata-Version: 2.4
2
+ Name: psse-utils
3
+ Version: 2026.6.0b1
4
+ Summary: A collection of small PSS/E utility scripts built on psse-model-util.
5
+ Project-URL: Homepage, https://github.com/ppsyOps/psse_utils
6
+ Project-URL: Repository, https://github.com/ppsyOps/psse_utils
7
+ Project-URL: Issues, https://github.com/ppsyOps/psse_utils/issues
8
+ Author: cadvena
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: PSS/E,flowgate,power systems,psse,scripts
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering
21
+ Requires-Python: !=3.14.1,<3.15,>=3.11
22
+ Requires-Dist: networkx
23
+ Requires-Dist: pandas
24
+ Requires-Dist: psse-model-util>=2026.4.5b1
25
+ Description-Content-Type: text/markdown
26
+
27
+ # psse-utils
28
+
29
+ A collection of small PSS/E utility scripts built on
30
+ [`psse-model-util`](https://pypi.org/project/psse-model-util/). Instead of a new
31
+ repo + PyPI project per script, scripts live here and ship as one package.
32
+
33
+ ## Installation
34
+
35
+ ~~~
36
+ pip install --pre psse-utils
37
+ ~~~
38
+
39
+ `--pre` is required while this package (and `psse-model-util`) are pre-releases.
40
+
41
+ ## Scripts
42
+
43
+ ### `flowgate-key-facilities`
44
+
45
+ Identify the **key facilities** for a set of PSS/E flowgates — the equipment
46
+ whose outage would most likely have a high impact on those flowgates. For each
47
+ flowgate in a `.mon` file, it keeps AC lines in a kV range and generators above a
48
+ PMax, all within `n` buses (hops) of the flowgate's elements, and writes the
49
+ results to CSVs.
50
+
51
+ ~~~
52
+ flowgate-key-facilities \
53
+ --mon flowgates.mon --raw Model.raw \
54
+ --areas 1 2 3 --sc SCA --out-dir outputs/
55
+ ~~~
56
+
57
+ Full options: `flowgate-key-facilities --help`. Outputs `branches.csv`,
58
+ `generators.csv`, `transformers_3w.csv`, `unresolved.csv` in `--out-dir`.
59
+
60
+ ## Adding a script
61
+
62
+ 1. Create `src/psse_utils/<name>.py` with the **logic as importable functions**
63
+ plus a thin `main(argv=None) -> int` that parses args and calls them (keeping
64
+ logic separate from the CLI leaves room for a future TUI/web UI).
65
+ 2. Add a console command in `pyproject.toml`:
66
+ `[project.scripts]` → `your-command = "psse_utils.<name>:main"`.
67
+ 3. Add tests `tests/test_<name>_*.py` (script-prefixed).
68
+ 4. Put any heavy/extra dependencies behind an extra in `[project.optional-dependencies]`.
69
+
70
+ ## Development
71
+
72
+ ~~~
73
+ python -m venv .venv
74
+ .venv\Scripts\python -m pip install --pre -e .
75
+ .venv\Scripts\python -m pip install pytest pytest-cov
76
+ .venv\Scripts\python -m pytest
77
+ ~~~
78
+
79
+ Versioning is CalVer (`YYYY.M.micro`) from `src/psse_utils/__about__.py`.
80
+ Releases publish to PyPI via Trusted Publishing — see the team setup runbook and
81
+ `psse-model-util`'s `docs/PUBLISHING.md`.
@@ -0,0 +1,55 @@
1
+ # psse-utils
2
+
3
+ A collection of small PSS/E utility scripts built on
4
+ [`psse-model-util`](https://pypi.org/project/psse-model-util/). Instead of a new
5
+ repo + PyPI project per script, scripts live here and ship as one package.
6
+
7
+ ## Installation
8
+
9
+ ~~~
10
+ pip install --pre psse-utils
11
+ ~~~
12
+
13
+ `--pre` is required while this package (and `psse-model-util`) are pre-releases.
14
+
15
+ ## Scripts
16
+
17
+ ### `flowgate-key-facilities`
18
+
19
+ Identify the **key facilities** for a set of PSS/E flowgates — the equipment
20
+ whose outage would most likely have a high impact on those flowgates. For each
21
+ flowgate in a `.mon` file, it keeps AC lines in a kV range and generators above a
22
+ PMax, all within `n` buses (hops) of the flowgate's elements, and writes the
23
+ results to CSVs.
24
+
25
+ ~~~
26
+ flowgate-key-facilities \
27
+ --mon flowgates.mon --raw Model.raw \
28
+ --areas 1 2 3 --sc SCA --out-dir outputs/
29
+ ~~~
30
+
31
+ Full options: `flowgate-key-facilities --help`. Outputs `branches.csv`,
32
+ `generators.csv`, `transformers_3w.csv`, `unresolved.csv` in `--out-dir`.
33
+
34
+ ## Adding a script
35
+
36
+ 1. Create `src/psse_utils/<name>.py` with the **logic as importable functions**
37
+ plus a thin `main(argv=None) -> int` that parses args and calls them (keeping
38
+ logic separate from the CLI leaves room for a future TUI/web UI).
39
+ 2. Add a console command in `pyproject.toml`:
40
+ `[project.scripts]` → `your-command = "psse_utils.<name>:main"`.
41
+ 3. Add tests `tests/test_<name>_*.py` (script-prefixed).
42
+ 4. Put any heavy/extra dependencies behind an extra in `[project.optional-dependencies]`.
43
+
44
+ ## Development
45
+
46
+ ~~~
47
+ python -m venv .venv
48
+ .venv\Scripts\python -m pip install --pre -e .
49
+ .venv\Scripts\python -m pip install pytest pytest-cov
50
+ .venv\Scripts\python -m pytest
51
+ ~~~
52
+
53
+ Versioning is CalVer (`YYYY.M.micro`) from `src/psse_utils/__about__.py`.
54
+ Releases publish to PyPI via Trusted Publishing — see the team setup runbook and
55
+ `psse-model-util`'s `docs/PUBLISHING.md`.
@@ -0,0 +1,423 @@
1
+ # psse-utils Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Repurpose the `flowgate_key_facilities` repo into **`psse-utils`** — a PyPI package that collects small PSS/E scripts built on `psse-model-util`, with the existing flowgate CLI as its first script.
6
+
7
+ **Architecture:** Restructure the single-module repo into a `src/psse_utils/` package (src layout, matching `psse-model-util`). Each script is a module with importable logic + a thin `main()`, exposed as its own console command. Reuse the repo's existing Trusted-Publishing `publish.yml`, pip-based `ci.yml`, and GitHub environments. Code changes land first on a branch; the GitHub-repo rename + PyPI pending-publisher (human/owner steps) and the publish happen after merge.
8
+
9
+ **Tech Stack:** Python 3.11+, Hatchling (build + CalVer version from `__about__.py`), pytest + pytest-cov, Trusted Publishing (OIDC) via GitHub Actions.
10
+
11
+ **Spec:** `docs/superpowers/specs/2026-06-23-psse-utils-design.md`.
12
+
13
+ **Baseline (current repo, branch `feat/psse-utils`):** single module `flowgate_key_facilities.py` at root; tests `tests/test_cli_args.py`, `tests/test_cli_main.py`, `tests/conftest.py`, `tests/data/`; `pyproject.toml` (name `flowgate-key-facilities`, static `version = "0.1.0b1"`); `.github/workflows/{ci,publish}.yml`; the flowgate CLI imports `psse_model_util` and is published as `flowgate-key-facilities 0.1.0b1` (a beta, to be yanked).
14
+
15
+ **Locked decisions:** repurpose (don't recreate); src layout; per-script console commands; logic separated from CLI; CalVer `YYYY.M.micro` (first release `2026.6.0b1`); keep the `flowgate-key-facilities` command and `flowgate_key_facilities.py` module name; script-prefixed test files; base dep `psse-model-util>=2026.4.5b1`.
16
+
17
+ ---
18
+
19
+ ## File structure (end state)
20
+
21
+ ```
22
+ src/psse_utils/
23
+ __init__.py # from psse_utils.__about__ import __version__
24
+ __about__.py # __version__ = "2026.6.0b1"
25
+ flowgate_key_facilities.py # moved from repo root (imports unchanged)
26
+ tests/
27
+ conftest.py # adds src/ to sys.path fallback
28
+ test_flowgate_key_facilities_args.py # was test_cli_args.py
29
+ test_flowgate_key_facilities_main.py # was test_cli_main.py
30
+ data/ Model_1.raw synthetic_flowgates.mon
31
+ pyproject.toml README.md LICENSE pdm.lock pdm.toml
32
+ .github/workflows/ci.yml publish.yml
33
+ docs/superpowers/{specs,plans}/...
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Phase 0 — Baseline
39
+
40
+ Work on the existing `feat/psse-utils` branch (already has the spec + this plan).
41
+
42
+ - [ ] **Step 0.1: Confirm the current suite is green before restructuring**
43
+
44
+ Run (from the repo root, using the project venv's python — `python -m pip install --pre -e .` first if needed):
45
+ ```
46
+ python -m pytest -q
47
+ ```
48
+ Expected: the existing flowgate tests pass (16 passed, ~100% coverage). If red, stop and fix before continuing.
49
+
50
+ ---
51
+
52
+ ## Phase 1 — Restructure into `src/psse_utils/`
53
+
54
+ **Files:** move `flowgate_key_facilities.py`; create `src/psse_utils/__init__.py`, `src/psse_utils/__about__.py`.
55
+
56
+ - [ ] **Step 1.1: Move the module into the package**
57
+
58
+ ```bash
59
+ mkdir -p src/psse_utils
60
+ git mv flowgate_key_facilities.py src/psse_utils/flowgate_key_facilities.py
61
+ ```
62
+ No code edits inside the file — its imports are `from psse_model_util... import`, unaffected by the move.
63
+
64
+ - [ ] **Step 1.2: Create the version source `src/psse_utils/__about__.py`**
65
+
66
+ ```python
67
+ """Package version — single source of truth for psse_utils versioning."""
68
+
69
+ __version__ = "2026.6.0b1"
70
+ ```
71
+
72
+ - [ ] **Step 1.3: Create `src/psse_utils/__init__.py`**
73
+
74
+ ```python
75
+ """psse_utils — a collection of small PSS/E utility scripts built on psse-model-util."""
76
+
77
+ from psse_utils.__about__ import __version__
78
+
79
+ __all__ = ["__version__"]
80
+ ```
81
+
82
+ - [ ] **Step 1.4: Commit**
83
+
84
+ ```bash
85
+ git add -A
86
+ git commit -m "refactor: move flowgate module into src/psse_utils package"
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Phase 2 — pyproject + workflows retarget
92
+
93
+ **Files:** `pyproject.toml`, `.github/workflows/publish.yml`. (`ci.yml` already does `pip install --pre -e .` and needs no change.)
94
+
95
+ - [ ] **Step 2.1: Rewrite the `pyproject.toml` `[project]` + tool tables**
96
+
97
+ Replace the existing `[project]` name/version/description/urls/scripts and the
98
+ `[tool.*]` packaging/test/coverage tables so the file reads:
99
+
100
+ ```toml
101
+ [project]
102
+ name = "psse-utils"
103
+ dynamic = ["version"]
104
+ description = "A collection of small PSS/E utility scripts built on psse-model-util."
105
+ readme = "README.md"
106
+ requires-python = ">=3.11,!=3.14.1,<3.15"
107
+ license = "MIT"
108
+ license-files = ["LICENSE"]
109
+ authors = [
110
+ { name = "cadvena" }
111
+ ]
112
+ keywords = ["psse", "power systems", "PSS/E", "scripts", "flowgate"]
113
+ classifiers = [
114
+ "Development Status :: 4 - Beta",
115
+ "Environment :: Console",
116
+ "Intended Audience :: Science/Research",
117
+ "License :: OSI Approved :: MIT License",
118
+ "Programming Language :: Python :: 3 :: Only",
119
+ "Programming Language :: Python :: 3.11",
120
+ "Programming Language :: Python :: 3.12",
121
+ "Programming Language :: Python :: 3.13",
122
+ "Topic :: Scientific/Engineering",
123
+ ]
124
+ dependencies = [
125
+ "psse-model-util>=2026.4.5b1",
126
+ "pandas",
127
+ "networkx",
128
+ ]
129
+
130
+ [project.urls]
131
+ Homepage = "https://github.com/ppsyOps/psse_utils"
132
+ Repository = "https://github.com/ppsyOps/psse_utils"
133
+ Issues = "https://github.com/ppsyOps/psse_utils/issues"
134
+
135
+ [project.scripts]
136
+ flowgate-key-facilities = "psse_utils.flowgate_key_facilities:main"
137
+
138
+ [build-system]
139
+ requires = ["hatchling"]
140
+ build-backend = "hatchling.build"
141
+
142
+ [tool.hatch.version]
143
+ path = "src/psse_utils/__about__.py"
144
+ pattern = "__version__ = ['\"](?P<version>[^'\"]+)['\"]"
145
+
146
+ [tool.hatch.build.targets.wheel]
147
+ packages = ["src/psse_utils"]
148
+
149
+ [tool.pdm.dev-dependencies]
150
+ test = [
151
+ "pytest>=8",
152
+ "pytest-cov",
153
+ ]
154
+
155
+ [tool.pytest.ini_options]
156
+ testpaths = ["tests"]
157
+ addopts = "--cov=psse_utils --cov-report=term-missing"
158
+
159
+ [tool.coverage.run]
160
+ source = ["src/psse_utils"]
161
+
162
+ [tool.coverage.report]
163
+ exclude_lines = [
164
+ "pragma: no cover",
165
+ "if __name__ == .__main__.:",
166
+ "raise NotImplementedError",
167
+ ]
168
+ fail_under = 95
169
+ ```
170
+
171
+ Notes: `version` becomes **dynamic** (sourced from `__about__.py`); the old
172
+ `pythonpath = ["."]` is dropped — `conftest.py` (Step 3.3) handles import path.
173
+
174
+ - [ ] **Step 2.2: Retarget `publish.yml` environment URLs to the new project**
175
+
176
+ In `.github/workflows/publish.yml`, change the two `url:` lines:
177
+ - `https://test.pypi.org/p/flowgate-key-facilities` → `https://test.pypi.org/p/psse-utils`
178
+ - `https://pypi.org/p/flowgate-key-facilities` → `https://pypi.org/p/psse-utils`
179
+
180
+ - [ ] **Step 2.3: Commit**
181
+
182
+ ```bash
183
+ git add pyproject.toml .github/workflows/publish.yml
184
+ git commit -m "build: rename distribution to psse-utils; CalVer dynamic version; src-layout packaging"
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Phase 3 — Tests: rename + fix imports
190
+
191
+ **Files:** rename the two test files; edit imports; update `tests/conftest.py`.
192
+
193
+ - [ ] **Step 3.1: Rename the test files (script-prefixed)**
194
+
195
+ ```bash
196
+ git mv tests/test_cli_args.py tests/test_flowgate_key_facilities_args.py
197
+ git mv tests/test_cli_main.py tests/test_flowgate_key_facilities_main.py
198
+ ```
199
+
200
+ - [ ] **Step 3.2: Update imports in the renamed tests**
201
+
202
+ In `tests/test_flowgate_key_facilities_args.py`: change
203
+ `from flowgate_key_facilities import _parse_args`
204
+ → `from psse_utils.flowgate_key_facilities import _parse_args`.
205
+
206
+ In `tests/test_flowgate_key_facilities_main.py`: change
207
+ `from flowgate_key_facilities import main`
208
+ → `from psse_utils.flowgate_key_facilities import main`.
209
+
210
+ (Also update the module-name references in their docstrings, `flowgate_key_facilities` →
211
+ `psse_utils.flowgate_key_facilities`, for accuracy.)
212
+
213
+ - [ ] **Step 3.3: Make `tests/conftest.py` add `src/` to `sys.path` (fallback when not installed)**
214
+
215
+ Replace `tests/conftest.py` with:
216
+ ```python
217
+ """Shared fixtures + import path for the psse_utils tests.
218
+
219
+ With the src/ layout the package is normally importable via the editable
220
+ install (`pip install -e .`). As a fallback for fresh checkouts, add src/ to
221
+ sys.path so `import psse_utils...` resolves.
222
+ """
223
+ import sys
224
+ from pathlib import Path
225
+
226
+ _SRC = Path(__file__).resolve().parent.parent / "src"
227
+ if _SRC.is_dir() and str(_SRC) not in sys.path:
228
+ sys.path.insert(0, str(_SRC))
229
+ ```
230
+ (Keep any existing fixtures from the old conftest below this block.)
231
+
232
+ - [ ] **Step 3.4: Reinstall editable + run the suite**
233
+
234
+ ```bash
235
+ python -m pip install --pre -e .
236
+ python -m pytest -q
237
+ ```
238
+ Expected: same tests pass (now importing from `psse_utils.flowgate_key_facilities`),
239
+ coverage measured on `psse_utils`, gate ≥ 95% met.
240
+
241
+ - [ ] **Step 3.5: Commit**
242
+
243
+ ```bash
244
+ git add -A
245
+ git commit -m "test: script-prefix flowgate test files; import from psse_utils; src/ conftest"
246
+ ```
247
+
248
+ ---
249
+
250
+ ## Phase 4 — README for the collection
251
+
252
+ **Files:** `README.md`.
253
+
254
+ - [ ] **Step 4.1: Rewrite `README.md`** so it describes the *collection*, lists the
255
+ flowgate script as the first entry, and documents install + how to add a script.
256
+
257
+ ```markdown
258
+ # psse-utils
259
+
260
+ A collection of small PSS/E utility scripts built on
261
+ [`psse-model-util`](https://pypi.org/project/psse-model-util/). Instead of a new
262
+ repo + PyPI project per script, scripts live here and ship as one package.
263
+
264
+ ## Installation
265
+
266
+ ~~~
267
+ pip install --pre psse-utils
268
+ ~~~
269
+
270
+ `--pre` is required while the package (and `psse-model-util`) are pre-releases.
271
+
272
+ ## Scripts
273
+
274
+ ### `flowgate-key-facilities`
275
+
276
+ Identify the key facilities for a set of PSS/E flowgates — equipment whose outage
277
+ would most impact those flowgates. For each flowgate in a `.mon` file it keeps AC
278
+ lines in a kV range and generators above a PMax, within `n` buses of the flowgate.
279
+
280
+ ~~~
281
+ flowgate-key-facilities --mon flowgates.mon --raw Model.raw \
282
+ --areas 1 2 3 --sc SCA --out-dir outputs/
283
+ ~~~
284
+
285
+ (Full options: `flowgate-key-facilities --help`.)
286
+
287
+ ## Adding a script
288
+
289
+ 1. Create `src/psse_utils/<name>.py` with importable functions (the logic) plus
290
+ a thin `main(argv=None) -> int` that parses args and calls them.
291
+ 2. Add a console command: `[project.scripts]` → `your-command = "psse_utils.<name>:main"`.
292
+ 3. Add tests `tests/test_<name>_*.py`.
293
+ 4. Put any heavy/extra dependencies behind an extra in `[project.optional-dependencies]`.
294
+
295
+ ## Development
296
+
297
+ ~~~
298
+ pip install -e ../psse_model_util # if developing the engine alongside
299
+ pip install --pre -e .
300
+ pytest
301
+ ~~~
302
+ ```
303
+
304
+ - [ ] **Step 4.2: Commit**
305
+
306
+ ```bash
307
+ git add README.md
308
+ git commit -m "docs: rewrite README for the psse-utils collection"
309
+ ```
310
+
311
+ ---
312
+
313
+ ## Phase 5 — Verify the package
314
+
315
+ - [ ] **Step 5.1: Build + inspect the wheel**
316
+
317
+ ```bash
318
+ python -m pip install --upgrade build
319
+ rm -rf dist && python -m build
320
+ python -c "import zipfile,glob; z=zipfile.ZipFile(sorted(glob.glob('dist/*.whl'))[-1]); print('\n'.join(n for n in z.namelist() if n.endswith(('.py','entry_points.txt','METADATA')))); print(z.read([n for n in z.namelist() if n.endswith('entry_points.txt')][0]).decode())"
321
+ ```
322
+ Expected: files live under `psse_utils/` (e.g. `psse_utils/flowgate_key_facilities.py`),
323
+ metadata `Name: psse-utils`, version `2026.6.0b1`, `Requires-Dist: psse-model-util>=2026.4.5b1`,
324
+ and entry point `flowgate-key-facilities = psse_utils.flowgate_key_facilities:main`.
325
+
326
+ - [ ] **Step 5.2: twine check**
327
+
328
+ ```bash
329
+ python -m pip install twine && python -m twine check dist/*
330
+ ```
331
+ Expected: both `PASSED`.
332
+
333
+ - [ ] **Step 5.3: Clean-venv install smoke test**
334
+
335
+ ```bash
336
+ python -m venv /tmp/psse-utils-verify
337
+ /tmp/psse-utils-verify/Scripts/python -m pip install --pre dist/psse_utils-*.whl
338
+ /tmp/psse-utils-verify/Scripts/python -c "import psse_utils; from psse_utils.flowgate_key_facilities import main; print('OK', psse_utils.__version__)"
339
+ /tmp/psse-utils-verify/Scripts/flowgate-key-facilities --help
340
+ ```
341
+ Expected: `OK 2026.6.0b1`; the console command prints its usage. (Windows paths shown; adjust for OS.)
342
+
343
+ - [ ] **Step 5.4: Re-lock pdm (dependency set unchanged, but project metadata changed)**
344
+
345
+ ```bash
346
+ python -m pdm lock
347
+ git add pdm.lock; git commit -m "build: refresh pdm.lock for psse-utils" || echo "no lock changes"
348
+ ```
349
+
350
+ ---
351
+
352
+ ## Phase 6 — (Optional, recommended) split flowgate logic from CLI
353
+
354
+ Sets the convention for future scripts and enables a future TUI/web UI. Keep tests green.
355
+
356
+ - [ ] **Step 6.1: Extract a `run(...)` function**
357
+
358
+ In `src/psse_utils/flowgate_key_facilities.py`, factor the body of `main()` (after
359
+ arg parsing) into a pure function, e.g.:
360
+ ```python
361
+ def run(*, mon, raw, areas, sc, out_dir, hops, kv_min, kv_max, gen_min_mw) -> dict:
362
+ """Resolve flowgates -> collect key facilities -> write CSVs; return the result dict."""
363
+ # (the existing pipeline body moves here)
364
+ ```
365
+ `main(argv)` then parses args and calls `run(**kwargs)`.
366
+
367
+ - [ ] **Step 6.2: Add a focused test for `run()`** in
368
+ `tests/test_flowgate_key_facilities_main.py` that calls `run(...)` directly with the
369
+ fixtures and asserts the output CSVs / returned dict — independent of argparse.
370
+
371
+ - [ ] **Step 6.3: Run + commit**
372
+
373
+ ```bash
374
+ python -m pytest -q
375
+ git add -A && git commit -m "refactor(flowgate): separate run() logic from main() CLI"
376
+ ```
377
+
378
+ ---
379
+
380
+ ## Phase 7 — Human / owner steps (cannot be automated by the agent)
381
+
382
+ These require GitHub-admin and PyPI-account actions. See
383
+ `OneDrive/Trapezia/PyPI-and-TestPyPI-setup-runbook.md`.
384
+
385
+ - [ ] **Step 7.1: Merge the branch** — open a PR `feat/psse-utils` → `master`, let CI pass, merge.
386
+
387
+ - [ ] **Step 7.2: Rename the GitHub repo** `flowgate_key_facilities` → `psse_utils`
388
+ (owner `cadvena`: GitHub Settings → Rename, or `gh repo rename psse_utils -R ppsyOps/flowgate_key_facilities`).
389
+ Then locally: `git remote set-url origin https://github.com/ppsyOps/psse_utils.git`,
390
+ rename the local clone folder, and recreate the venv with stdlib `python -m venv`.
391
+
392
+ - [ ] **Step 7.3: Add the `psse-utils` pending publishers** on PyPI and TestPyPI —
393
+ Project `psse-utils`, Owner `ppsyOps`, **Repo `psse_utils`**, Workflow `publish.yml`,
394
+ Env `pypi` / `testpypi`. (GitHub environments `pypi`/`testpypi` already exist on the repo.)
395
+
396
+ ---
397
+
398
+ ## Phase 8 — Publish
399
+
400
+ - [ ] **Step 8.1: Dispatch the publish workflow**
401
+
402
+ `gh workflow run publish.yml --ref master --repo ppsyOps/psse_utils`
403
+ (Manual dispatch — a `cd.yml`/token-created release can't trigger it.)
404
+
405
+ - [ ] **Step 8.2: TestPyPI → verify → approve PyPI gate**
406
+
407
+ Build → TestPyPI publishes automatically; verify
408
+ `pip install --pre --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ psse-utils`
409
+ in a clean venv. Then approve the `pypi` deployment (reviewer `cadvena`) to publish to real PyPI.
410
+
411
+ - [ ] **Step 8.3: Confirm + clean up**
412
+
413
+ `pip install --pre psse-utils` from real PyPI; `flowgate-key-facilities --help` works.
414
+ Then **yank** the orphaned `flowgate-key-facilities 0.1.0b1` on PyPI (project → Manage → yank),
415
+ since its functionality now ships in `psse-utils`.
416
+
417
+ ---
418
+
419
+ ## Self-review notes
420
+
421
+ - **Spec coverage:** repurpose (P1,P7.2) · src layout (P1,P2) · per-script command (P2.1 scripts) · logic/CLI split (P6) · CalVer `2026.6.0b1` (P1.2,P2.1) · keep flowgate command+module names (P2.1,P1.1) · script-prefixed tests (P3) · `psse-model-util` dep + extras note (P2.1, README P4) · reuse publish/ci/envs (P2.2, P7) · yank flowgate beta (P8.3) · PyPI publisher (P7.3). ✔
422
+ - **Placeholders:** none — every code/edit step shows the content. The only "optional" is Phase 6, explicitly marked.
423
+ - **Consistency:** distribution `psse-utils`, import `psse_utils`, module `psse_utils.flowgate_key_facilities`, command `flowgate-key-facilities`, version `2026.6.0b1`, repo `ppsyOps/psse_utils` — used uniformly across tasks.