diff --git a/_bmad-output/implementation-artifacts/1-1-project-scaffold-and-verified-entry-points.md b/_bmad-output/implementation-artifacts/1-1-project-scaffold-and-verified-entry-points.md new file mode 100644 index 0000000..1bf1d20 --- /dev/null +++ b/_bmad-output/implementation-artifacts/1-1-project-scaffold-and-verified-entry-points.md @@ -0,0 +1,110 @@ +# 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. diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 93eb2ad..52bb227 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -43,8 +43,8 @@ story_location: _bmad-output/implementation-artifacts development_status: # Epic 1: Device Setup & Provisioning - epic-1: backlog - 1-1-project-scaffold-and-verified-entry-points: backlog + epic-1: in-progress + 1-1-project-scaffold-and-verified-entry-points: ready-for-dev 1-2-configuration-read-write-wipe: backlog 1-3-wifi-hotspot-and-captive-portal-form: backlog 1-4-location-resolution-icao-and-address: backlog