Two-cycle stale→recovery tests confirm that a non-empty fetch after timeout or
empty-list correctly resets is_stale=False; no changes to main.py or aircraft.py
required as recovery logic was already correct from story 3-1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 10 review criteria pass. No code changes required: implementation
matches spec exactly. Added two deferred-work items for only
requests.Timeout being caught (other fetch errors propagate without
stale marking) and the growing _run_one_cycle signature (RendererState
dataclass suggested for future). 103 tests pass, ruff clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When fetch times out or returns empty after prior data, retain last aircraft
with is_stale=True and render them as black outlines so the display stays
informative rather than blank or crashing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 10 review criteria pass with no code changes required. 99 tests
pass, ruff clean. Added 4 deferred items (WaveshareDisplay stub, HAT
crash-on-boot, dumb fixed sleep, startup text position). Epic-2
marked done as all 7 stories are now in done state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements story 2-7: full main.py with _make_startup_screen(),
_run_one_cycle() with per-phase timing and slow-render warning, and
main() connecting config → bounds → fetcher → renderer → display.
Adds provision.py early-exit when already provisioned. Adds User=root
to both systemd service files. Adds tests/test_main.py (3 new tests,
99 total). Updates scaffold test to allow provisioning.config import
from main.py. All quality gates pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Code review of stateful Renderer and DisplayInterface: all 10 review
criteria pass with no code changes required. Add two deferred items for
WaveshareDisplay NotImplementedError stub (wired in 2-7) and pixel-space
trail staleness on re-provisioning. Mark story done in story file and
sprint-status.yaml.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements story 2-6: Renderer class with per-aircraft trail history
(deque capped at TRAIL_MAX_DOTS=5), NullDisplay with DEBUG logging,
WaveshareDisplay stub, and end-to-end pipeline smoke test. All 96
tests pass; ruff check and format clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements draw_aircraft() with heading arrow (filled/outlined for MLAT),
callsign+altitude label, and a 5-dot fade trail. Adds 5 tests covering
all ACs; all 94 tests pass, ruff lint and format clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 7 ACs verified. No code fixes required: boundary logic (<=), category
map, callsign prefix guard, altitude fallback thresholds, empty-category
guard, and type annotations are all correct. 89/89 tests pass, ruff clean.
Added two deferred items: hardcoded _AIRLINE_PREFIXES subset and A-category
military aircraft misclassification. Story and sprint-status marked done.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 10 review criteria pass without any code fixes required. Two tech-debt
items added to deferred-work: non-Polygon geometry types silently skipped
(intentional for MVP) and null-geometry GeoJSON features would raise
AttributeError (acceptable for controlled OpenAIP input).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds draw_home_marker() in overlay.py (red cross at projected home
position) and draw_airspace() in airspace.py (GeoJSON Polygon
boundaries in blue, graceful FileNotFoundError handling). Includes
airspace fixture and three tests covering all acceptance criteria.
All 70 tests pass; ruff check and format clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 10 review criteria pass without fixes. Deferred two tech-debt items
(equirectangular distortion at high latitudes, missing dimension assertion
in basemap.load()). Story and sprint-status marked done.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add MapBounds dataclass and equirectangular project() function in
projection.py, basemap.load() forcing pixels into memory via .copy(),
and full test coverage for both modules (4 new tests). All quality
gates pass: 67 tests, ruff clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 5 ACs verified clean: field mapping, safe defaults, timeout propagation,
MLAT detection, and FileFixtureFetcher. 64 tests pass, ruff check and format
clean. Sprint status updated to done, deferred-work updated with runtime
verification note for HttpFetcher on live dump1090.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add HttpFetcher and FileFixtureFetcher with shared _parse_aircraft helper,
DUMP1090_URL constant, realistic fixture data, and full test coverage for
all acceptance criteria (AC1–AC5).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 5 ACs verified: ICAO lookup via importlib.resources, Nominatim
geocoding with User-Agent and timeout, exact error messages, mocked
HTTP in tests, portal POST /find-location returns 200 on success and
error. 45 tests pass, ruff clean. Sprint status updated to done.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds location.resolve() with ICAO CSV lookup and Nominatim geocoding,
POST /find-location portal route with inline confirmation/error display,
and full test coverage with mocked network calls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement wifi.py (start_ap/stop_ap/kill_wifi with subprocess and ProvisioningError),
portal.py (Flask captive portal with Android/iOS/Windows probe redirects and 404 catch-all),
updated provision.py provisioning loop, and full test suite (38 passing).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 4 ACs verified, 7 tests pass, ruff clean. No issues found.
Story status updated to done; no new deferred items.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add provisioning/config.py with read/write/wipe functions, update
conftest.py with autouse CONFIG_PATH patch fixture, write 7 tests
covering all acceptance criteria, and extend pyproject.toml
per-file-ignores so conftest.py may import from provisioning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces placeholder stubs in test_models.py, test_gpio_ctrl.py, and
provisioning/test_provision_loop.py with real assertions. Adds new
test_scaffold.py covering AC3 file-presence, importlib.resources
airports.csv load, constants completeness, and main.py provisioning
import guard. Extends ruff per-file-ignores so tests/provisioning/*.py
may import from planemapper.provisioning. All 22 tests pass; ruff
check and format --check both clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Initialises implementation-artifacts/ with sprint-status.yaml covering all
14 stories across 4 epics. Promotes architecture.md and epics.md from
untracked state. Sprint 1 branch is now ready for autopilot execution.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>