Files
planeMapper/_bmad-output/implementation-artifacts/1-1-project-scaffold-and-verified-entry-points.md
T
2026-04-22 22:22:39 -04:00

111 lines
7.8 KiB
Markdown

# Story 1.1: Project Scaffold & Verified Entry Points
Status: ready-for-dev
## Story
As a developer,
I want a verified project scaffold with the `src/planemapper/` layout, both console entry points installable, all module stubs in place, systemd unit files, and `pytest` running without error,
So that every subsequent story has a consistent, working foundation to build on.
## Acceptance Criteria
1. **Given** the repository is cloned on a Pi Zero 2W running Raspberry Pi OS Bookworm **When** `pip install -e .` is run **Then** it completes without errors and both `planemapper-provision` and `planemapper-radar` commands are available on PATH **And** running either command logs "not implemented" and exits with code 0
2. **Given** the project is installed **When** `pytest` is run **Then** the test suite discovers tests and exits with 0 failures (empty stubs acceptable)
3. **Given** the project structure **When** a developer inspects the repository **Then** all files from the Architecture directory structure exist: `src/planemapper/` with `__init__.py`, `constants.py`, `models.py`, `main.py`, `provision.py`, `fetcher.py`, `gpio_ctrl.py`, `display.py`, `provisioning/` (7 modules), `renderer/` (8 modules), `data/airports.csv`; `systemd/` with both `.service` files; `pyproject.toml`, `requirements.txt`, `requirements-dev.txt` **And** `src/planemapper/data/airports.csv` is accessible via `importlib.resources` **And** `ruff check .` passes with zero violations
## 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.*`
- [ ] 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]`
- [ ] 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
- [ ] 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`
- [ ] 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`
- [ ] 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 .`
- [ ] 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`
- [ ] 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
- [ ] 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
## Dev Notes
### Critical Context
**Architecture constraints:**
- Package layout uses `src/` layout: `src/planemapper/` — ensure `pyproject.toml` specifies `package-dir = {"" = "src"}` so `pip install -e .` finds the package.
- The `main.py` entry point **must not** import anything from `planemapper.provisioning.*`. This boundary is enforced by ruff; any violation will cause `ruff check .` to fail.
- `airports.csv` must be bundled as package data and accessed via `importlib.resources`, not via a raw file path relative to the source tree, so it works correctly after installation.
- All stub `main()` functions must log "not implemented" (using Python `logging`, not `print`) and return (exit code 0). Do not raise `NotImplementedError`.
**Pinned dependency versions (do not deviate):**
- Runtime: `Pillow==12.2.0`, `gpiozero==2.0.1`, `Flask==3.1.3`, `requests==2.33.1`
- Dev: `pytest==9.0.3`, `ruff==0.15.11`, `gpiozero[mock]`
- Python: `>=3.11`
**Systemd unit design:**
- `planemapper-provision.service`: `Type=oneshot` — runs once and exits; guards `planemapper-radar.service` startup
- `planemapper-radar.service`: `Restart=always`, `After=planemapper-provision.service` — long-running radar loop
**ruff configuration:**
- `line-length = 100`
- `target-version = "py311"`
- Import boundary rule: `planemapper.main` may not import from `planemapper.provisioning.*` (use `ruff`'s `flake8-tidy-imports` or equivalent `banned-module-level-imports` setting)
**Testing stance for this story:**
- Tests are stubs only — each file must have at least one discoverable test function (even if it just calls `pass`) so pytest exits 0 with no collection errors.
- No actual logic needs to be tested in Story 1.1; that begins in Story 1.2 onwards.