Landscaping Assistant
Vision
Property management app for a landscaping business operating in Utah County and SLC. Track client properties, their addresses, services (edging & trimming, mowing, weeding), and per-property notes. Address lookup via Nominatim geocoding so you can enter a house number and get the full verified address. Built with Ruby on Rails to learn the framework while shipping a real tool.
User Stories
| Key | Role | Summary | Success Metric |
|---|---|---|---|
| address-lookup | Landscaper | Enter house number, get full address | Address auto-fills from Nominatim results |
| property-crud | Landscaper | Add/edit/delete client properties | Full CRUD with search and filter |
| service-tracking | Landscaper | Assign services to properties | Checkboxes for edging/mowing/weeding per property |
| special-notes | Landscaper | Per-property free-text notes | Notes visible on property detail page |
| work-queue | Landscaper | Queue properties for today and reorder route | Drag-to-reorder, complete/uncomplete toggle, Turbo Stream updates |
| weekly-tracking | Landscaper | Track weekly completion across all active properties | Week view shows completed/total, inactive properties excluded |
| schedule-upload | Landscaper | Snap a photo of the paper schedule for later reference | Upload photo, view thumbnails, editable captions, MinIO storage in prod |
| comments | All roles | Threaded discussion on property detail pages with photo attachments, reply threads, and activity-based sort | Comments support photos, one-level threading, newest-activity-first sort. Umbrella: #303. Children: #302 (camera crash), #304 (sort order) |
| saas | All roles | SaaS subscription, business codes for crew onboarding, admin approval of join requests, Apple IAP billing | Business admin generates join code, crew enters code to request access, admin approves/denies. Apple IAP subscription at $49.99/mo. Umbrella: #308. Children: #309 (join form), #310 (admin approval), #312 (Keycloak registration field) |
| auth | All roles | Log in with Keycloak, see role-appropriate tabs and features | Auth Code + PKCE login, role-based nav (5-slot grid), PaperTrail audit trail, graceful degradation without Keycloak |
| account-email | All roles | Receive password reset and email verification emails via Postmark SMTP | 100% of password reset requests result in delivered email (verified via Postmark Activity feed). Story note: <code>story-landscaping-assistant-account-email</code> |
| multi-tenancy | Platform owner | Business model with row-level property isolation so multiple landscaping companies can use the app without seeing each other's data | Properties scoped to current_business behind multi_tenancy feature flag. Second test business (Greenview Landscaping) fully isolated from D&T Lawn Rangers. Umbrella: #282. Children: #294 (model), #295 (scoping), #296 (seed data) |
| custom-domain | All roles | Access app via landscaping-assistant.app instead of Tailscale hostname. DNS A record to Hetzner edge VPS (godaddy-tofu/pal-e-platform), Caddy reverse proxy (Salt state), Rails config.hosts + Keycloak redirect URI updates | landscaping-assistant.app resolves and serves the app publicly. Umbrella: landscaping-assistant#223. Children: pal-e-platform#434 (Caddy), landscaping-assistant#224 (Rails+Keycloak) |
| deploy-validation | Platform owner | Staging environment that auto-deploys every commit for deploy validation via observability before promoting to prod | staging.landscaping-assistant.app serves the app, golden signals visible in Grafana, alerts fire on broken deploys, prod remains manual promotion. Parent: #342 |
| app-store-submission | All roles | Submit and maintain iOS App Store listing (privacy declarations, review compliance, metadata) | App approved and published on App Store |
| ci-performance | Developer | CI pipelines reuse pre-compiled gems from base image for faster builds | bundle-install step seeds from pre-baked gems, pipeline time reduced |
| crew-time-tracking | Crew Member, Crew Lead | Clock in/out, track breaks, view daily/weekly hours from Hours tab (nav slot 2). BusyBusy-inspired time tracking. | 100% of crew can clock in, break, and clock out with accurate time totals. Story note: <code>story-landscaping-assistant-crew-time-tracking</code>. Parent: #331 |
Architecture
Monolithic Rails 8.1.3 app with Hotwire (Turbo + Stimulus). PostgreSQL via shared CNPG cluster. Nominatim for geocoding. ActiveStorage + MinIO for photo uploads.
- Domain: Property, Service, PropertyService (join), WorkQueueItem, Upload
- Turbo Streams for queue add/remove/complete toggle
- Stimulus controllers: location, filter, sortable, add-location, week-move, upload
- Five-tab bottom nav: Today / Week / New / Properties / Photos
- ActiveStorage with S3-compatible MinIO backend in prod, local disk in dev
- No separate frontend -- Rails serves HTML directly
Board
Primary kanban:
Observability kanban:
board-landscaping-assistantObservability kanban:
board-landscaping-observability (dedicated project: landscaping-observability)Observability
Target: full Datadog-equivalent observability using the open-source Grafana ecosystem. Benchmark table tracks progress against every Datadog capability. Detailed rollout plan in
docs/observability-roadmap.md. Parent issue: #43.| Datadog Capability | Our Equivalent | Status | Issue |
|---|---|---|---|
| Infrastructure Monitoring | kube-prometheus-stack + node-exporter | COMPLETE | -- |
| Log Management | Loki + Promtail + Grafana Explore | COMPLETE | -- |
| Application Metrics | yabeda-rails + yabeda-prometheus | COMPLETE | #19 (done) |
| Dashboards | Grafana (26 dashboards + golden signals) | COMPLETE | #16 (done) |
| Alerting | Alertmanager -> Telegram + Slack | PARTIAL | #17 (backlog) |
| CI Visibility | DORA exporter + Woodpecker API | COMPLETE | #20 (done) |
| Synthetic Monitoring | Blackbox exporter (13 probes) | COMPLETE | #21 (done) |
| Container Scanning | Harbor Trivy | COMPLETE | -- |
| APM / Distributed Tracing | OpenTelemetry + Grafana Tempo | NOT STARTED | #85 |
| Database Monitoring (query-level) | pg_stat_statements + Grafana dashboard | NOT STARTED | #86 |
| Continuous Profiling | Grafana Pyroscope | NOT STARTED | #87 |
| Infrastructure Visualization | Headlamp + Grafana NodeGraph | NOT STARTED | #88 |
| RUM (Frontend) | Grafana Faro | NOT STARTED | #89 |
| SLOs / Error Budgets | Pyrra | NOT STARTED | #90 |
| Runtime Security | Falco | NOT STARTED | #90 |