Implement story 1.1: project scaffold and verified entry points

Creates the complete src/planemapper/ layout with all module stubs,
provisioning and renderer subpackages, airports.csv data bundle,
systemd unit files, and full test scaffold. All three quality gates
pass: pytest 12/12, ruff check zero violations, ruff format clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Edholm
2026-04-22 22:26:53 -04:00
parent 0612e0fe02
commit 85c8acf767
71 changed files with 85541 additions and 56 deletions
@@ -1,6 +1,6 @@
# Story 1.1: Project Scaffold & Verified Entry Points
Status: ready-for-dev
Status: review
## Story
@@ -18,68 +18,68 @@ So that every subsequent story has a consistent, working foundation to build on.
## Tasks / Subtasks
- [ ] Task 1: Create `pyproject.toml` (AC: #1, #3)
- [ ] 1.1 Set `[build-system]` to use `setuptools` with `find_packages`
- [ ] 1.2 Set `requires-python = ">=3.11"` and list pinned runtime dependencies (Pillow==12.2.0, gpiozero==2.0.1, Flask==3.1.3, requests==2.33.1)
- [ ] 1.3 Add `[project.scripts]` with `planemapper-radar = "planemapper.main:main"` and `planemapper-provision = "planemapper.provision:main"`
- [ ] 1.4 Add `[tool.setuptools.package-data]` entry so `planemapper/data/airports.csv` is included in the installed package
- [ ] 1.5 Add `[tool.ruff]` section: `line-length = 100`, `target-version = "py311"`, and an import boundary rule preventing `planemapper.main` from importing `planemapper.provisioning.*`
- [x] Task 1: Create `pyproject.toml` (AC: #1, #3)
- [x] 1.1 Set `[build-system]` to use `setuptools` with `find_packages`
- [x] 1.2 Set `requires-python = ">=3.11"` and list pinned runtime dependencies (Pillow==12.2.0, gpiozero==2.0.1, Flask==3.1.3, requests==2.33.1)
- [x] 1.3 Add `[project.scripts]` with `planemapper-radar = "planemapper.main:main"` and `planemapper-provision = "planemapper.provision:main"`
- [x] 1.4 Add `[tool.setuptools.package-data]` entry so `planemapper/data/airports.csv` is included in the installed package
- [x] 1.5 Add `[tool.ruff]` section: `line-length = 100`, `target-version = "py311"`, and an import boundary rule preventing `planemapper.main` from importing `planemapper.provisioning.*`
- [ ] Task 2: Create `requirements.txt` and `requirements-dev.txt` (AC: #1, #3)
- [ ] 2.1 `requirements.txt`: pin Pillow==12.2.0, gpiozero==2.0.1, Flask==3.1.3, requests==2.33.1
- [ ] 2.2 `requirements-dev.txt`: pin pytest==9.0.3, ruff==0.15.11, add `gpiozero[mock]`
- [x] Task 2: Create `requirements.txt` and `requirements-dev.txt` (AC: #1, #3)
- [x] 2.1 `requirements.txt`: pin Pillow==12.2.0, gpiozero==2.0.1, Flask==3.1.3, requests==2.33.1
- [x] 2.2 `requirements-dev.txt`: pin pytest==9.0.3, ruff==0.15.11, add `gpiozero[mock]`
- [ ] Task 3: Create top-level `src/planemapper/` module stubs (AC: #1, #3)
- [ ] 3.1 `src/planemapper/__init__.py` — empty or version string only
- [ ] 3.2 `src/planemapper/constants.py` — stub with module docstring and placeholder constants
- [ ] 3.3 `src/planemapper/models.py` — stub with module docstring
- [ ] 3.4 `src/planemapper/fetcher.py` — stub with module docstring
- [ ] 3.5 `src/planemapper/gpio_ctrl.py` — stub with module docstring
- [ ] 3.6 `src/planemapper/display.py` — stub with module docstring
- [ ] 3.7 `src/planemapper/main.py``main()` function that logs "not implemented" and returns; must NOT import from `planemapper.provisioning.*`
- [ ] 3.8 `src/planemapper/provision.py``main()` function that logs "not implemented" and returns
- [x] Task 3: Create top-level `src/planemapper/` module stubs (AC: #1, #3)
- [x] 3.1 `src/planemapper/__init__.py` — empty or version string only
- [x] 3.2 `src/planemapper/constants.py` — stub with module docstring and placeholder constants
- [x] 3.3 `src/planemapper/models.py` — stub with module docstring
- [x] 3.4 `src/planemapper/fetcher.py` — stub with module docstring
- [x] 3.5 `src/planemapper/gpio_ctrl.py` — stub with module docstring
- [x] 3.6 `src/planemapper/display.py` — stub with module docstring
- [x] 3.7 `src/planemapper/main.py``main()` function that logs "not implemented" and returns; must NOT import from `planemapper.provisioning.*`
- [x] 3.8 `src/planemapper/provision.py``main()` function that logs "not implemented" and returns
- [ ] Task 4: Create `provisioning/` subpackage stubs (AC: #3)
- [ ] 4.1 `src/planemapper/provisioning/__init__.py`
- [ ] 4.2 `src/planemapper/provisioning/portal.py`
- [ ] 4.3 `src/planemapper/provisioning/location.py`
- [ ] 4.4 `src/planemapper/provisioning/tiles.py`
- [ ] 4.5 `src/planemapper/provisioning/airspace.py`
- [ ] 4.6 `src/planemapper/provisioning/wifi.py`
- [ ] 4.7 `src/planemapper/provisioning/config.py`
- [x] Task 4: Create `provisioning/` subpackage stubs (AC: #3)
- [x] 4.1 `src/planemapper/provisioning/__init__.py`
- [x] 4.2 `src/planemapper/provisioning/portal.py`
- [x] 4.3 `src/planemapper/provisioning/location.py`
- [x] 4.4 `src/planemapper/provisioning/tiles.py`
- [x] 4.5 `src/planemapper/provisioning/airspace.py`
- [x] 4.6 `src/planemapper/provisioning/wifi.py`
- [x] 4.7 `src/planemapper/provisioning/config.py`
- [ ] Task 5: Create `renderer/` subpackage stubs (AC: #3)
- [ ] 5.1 `src/planemapper/renderer/__init__.py`
- [ ] 5.2 `src/planemapper/renderer/renderer.py`
- [ ] 5.3 `src/planemapper/renderer/projection.py`
- [ ] 5.4 `src/planemapper/renderer/basemap.py`
- [ ] 5.5 `src/planemapper/renderer/aircraft.py`
- [ ] 5.6 `src/planemapper/renderer/airspace.py`
- [ ] 5.7 `src/planemapper/renderer/colours.py`
- [ ] 5.8 `src/planemapper/renderer/icons.py`
- [x] Task 5: Create `renderer/` subpackage stubs (AC: #3)
- [x] 5.1 `src/planemapper/renderer/__init__.py`
- [x] 5.2 `src/planemapper/renderer/renderer.py`
- [x] 5.3 `src/planemapper/renderer/projection.py`
- [x] 5.4 `src/planemapper/renderer/basemap.py`
- [x] 5.5 `src/planemapper/renderer/aircraft.py`
- [x] 5.6 `src/planemapper/renderer/airspace.py`
- [x] 5.7 `src/planemapper/renderer/colours.py`
- [x] 5.8 `src/planemapper/renderer/icons.py`
- [ ] Task 6: Bundle `airports.csv` data file (AC: #3)
- [ ] 6.1 Download `airports.csv` from OurAirports (https://ourairports.com/data/airports.csv) and place at `src/planemapper/data/airports.csv`
- [ ] 6.2 Confirm `pyproject.toml` package-data entry covers `data/airports.csv`
- [ ] 6.3 Smoke-test `importlib.resources` access in a scratch script or test to confirm the file is reachable after `pip install -e .`
- [x] Task 6: Bundle `airports.csv` data file (AC: #3)
- [x] 6.1 Download `airports.csv` from OurAirports (https://ourairports.com/data/airports.csv) and place at `src/planemapper/data/airports.csv`
- [x] 6.2 Confirm `pyproject.toml` package-data entry covers `data/airports.csv`
- [x] 6.3 Smoke-test `importlib.resources` access in a scratch script or test to confirm the file is reachable after `pip install -e .`
- [ ] Task 7: Create `systemd/` unit files (AC: #3)
- [ ] 7.1 `systemd/planemapper-provision.service`: `Type=oneshot`, runs `planemapper-provision`; intended to run at first boot / post-reset; include `[Install]` target
- [ ] 7.2 `systemd/planemapper-radar.service`: `Restart=always`, `After=planemapper-provision.service`; runs `planemapper-radar`
- [x] Task 7: Create `systemd/` unit files (AC: #3)
- [x] 7.1 `systemd/planemapper-provision.service`: `Type=oneshot`, runs `planemapper-provision`; intended to run at first boot / post-reset; include `[Install]` target
- [x] 7.2 `systemd/planemapper-radar.service`: `Restart=always`, `After=planemapper-provision.service`; runs `planemapper-radar`
- [ ] Task 8: Create `tests/` structure (AC: #2)
- [ ] 8.1 `tests/conftest.py` — empty or with a minimal shared fixture comment
- [ ] 8.2 `tests/fixtures/aircraft_sample.json` — minimal valid JSON stub (empty list acceptable)
- [ ] 8.3 `tests/fixtures/airspace_sample.geojson` — minimal valid GeoJSON stub
- [ ] 8.4 Top-level test stubs: `test_fetcher.py`, `test_models.py`, `test_projection.py`, `test_colours.py`, `test_icons.py`, `test_renderer.py`, `test_pipeline.py`, `test_gpio_ctrl.py` — each contains at least one `pass`-body test function so pytest can discover them
- [ ] 8.5 `tests/provisioning/__init__.py` (empty) plus `test_location.py`, `test_tiles.py`, `test_config.py`, `test_provision_loop.py` with stub test functions
- [x] Task 8: Create `tests/` structure (AC: #2)
- [x] 8.1 `tests/conftest.py` — empty or with a minimal shared fixture comment
- [x] 8.2 `tests/fixtures/aircraft_sample.json` — minimal valid JSON stub (empty list acceptable)
- [x] 8.3 `tests/fixtures/airspace_sample.geojson` — minimal valid GeoJSON stub
- [x] 8.4 Top-level test stubs: `test_fetcher.py`, `test_models.py`, `test_projection.py`, `test_colours.py`, `test_icons.py`, `test_renderer.py`, `test_pipeline.py`, `test_gpio_ctrl.py` — each contains at least one `pass`-body test function so pytest can discover them
- [x] 8.5 `tests/provisioning/__init__.py` (empty) plus `test_location.py`, `test_tiles.py`, `test_config.py`, `test_provision_loop.py` with stub test functions
- [ ] Task 9: Verify quality gates pass (AC: #1, #2, #3)
- [ ] 9.1 Run `pip install -e .` and confirm both entry-point commands exist on PATH
- [ ] 9.2 Run `planemapper-radar` and `planemapper-provision`; confirm each logs "not implemented" and exits 0
- [ ] 9.3 Run `pytest` and confirm zero failures
- [ ] 9.4 Run `ruff check .` and confirm zero violations
- [ ] 9.5 Run `ruff format --check .` and confirm zero formatting issues
- [x] Task 9: Verify quality gates pass (AC: #1, #2, #3)
- [x] 9.1 Run `pip install -e .` and confirm both entry-point commands exist on PATH
- [x] 9.2 Run `planemapper-radar` and `planemapper-provision`; confirm each logs "not implemented" and exits 0
- [x] 9.3 Run `pytest` and confirm zero failures
- [x] 9.4 Run `ruff check .` and confirm zero violations
- [x] 9.5 Run `ruff format --check .` and confirm zero formatting issues
## Dev Notes
@@ -44,7 +44,7 @@ story_location: _bmad-output/implementation-artifacts
development_status:
# Epic 1: Device Setup & Provisioning
epic-1: in-progress
1-1-project-scaffold-and-verified-entry-points: ready-for-dev
1-1-project-scaffold-and-verified-entry-points: review
1-2-configuration-read-write-wipe: backlog
1-3-wifi-hotspot-and-captive-portal-form: backlog
1-4-location-resolution-icao-and-address: backlog