Canvassing Launch Readiness
Production configuration, native credentials, privacy controls, verification gates, and physical-device tests required before enabling canvassing.
Canvassing Launch Readiness
This is a release checklist. Source code, automated tests, or a successful local export do not prove that database migrations, server functions, native binaries, store credentials, or background location behavior are live in production.
Do not enable canvassing for a live organization until every applicable item below is confirmed in the target environment.
1. Database and Server
- Apply the complete canvassing migration chain in timestamp order. It includes the foundation, proposal-delivery idempotency and privacy, estimate scheduling and customer notes, geocode cache, manager workflows and settings, server authority, missing-property intake, authenticated weighted rate limits, atomic AI quota/replay, scalable retention, 12-meter import deduplication, incremental rollups, durable visit claims, field-integrity evidence, verified goal/leaderboard progress, immutable GPS replay fingerprints, visit-request ledger retention, and the ordered release tail:
20260723254000_organization_reporting_timezone.sql,20260723255000_canvassing_filtered_leaderboard.sql,20260723256000_canvassing_representative_drill.sql,20260723257000_canvassing_leaderboard_emphasis.sql,20260723258000_pricing_catalog_rls_hardening.sql,20260723259000_canonical_estimate_acceptance.sql,20260723260000_estimate_acceptance_revision.sql,20260723261000_mobile_residential_proposal_atomic_sync.sql,20260723262000_proposal_delivery_revision_guard.sql,20260723262500_mobile_estimate_photo_idempotency.sql,20260723263000_public_web_canonical_acceptance.sql,20260723263500_job_photo_storage_hardening.sql,20260723264000_signed_estimate_contract_immutability.sql,20260723265000_estimate_acceptance_reconciliation_lease.sql,20260723266000_estimate_acceptance_outbox.sql,20260723267000_database_runtime_lint_hardening.sql,20260723268000_canvassing_bulk_target_rollup_scale.sql, and20260723269000_service_role_runtime_grants.sql. - Confirm PostGIS is available and the spatial columns and indexes were created.
- Confirm RLS is enabled on every canvassing table.
- Verify the target environment uses the intended live Supabase project for the web app, mobile app, Edge Functions, Auth, and Storage.
- Apply the chain to a production-like staging database running real PostgreSQL with PostGIS. Run the SQL/RLS test suite against that database and verify functions, triggers, transition-table rollups, constraints, indexes, grants, service-only routines, tenant isolation, and cron jobs. Source-text migration tests are not a substitute.
- The clean local Supabase PostgreSQL 17/PostGIS chain through seed now passes with zero database-lint errors and a 24-test pgTAP authorization and scale matrix. CI repeats the clean apply, lint, and pgTAP checks on every change. The matrix includes a 10,000-target single-statement insert, exact target/counter/disposition checks, and trusted service-role table/sequence privileges. This proves clean-schema compatibility but does not replace applying the chain to an upgraded production snapshot in the target staging environment.
- Confirm the raw-location retention job exists and makes progress in bounded batches while preserving shift summaries. When
pg_cronis unavailable, invokecanvassing_run_location_retentionhourly from an authorized service-role scheduler. - Confirm the visit-request retention job removes only completed claims older than 45 days, records bounded backlog health, and preserves reserved/recoverable claims. When
pg_cronis unavailable, invokecanvassing_run_visit_request_retention(10,5000)hourly from an authorized service-role scheduler. - Confirm the proposal-delivery ledger retention job removes requests and recipient PII older than 90 days. When
pg_cronis unavailable, invokeproposal_delivery_run_retentionhourly from an authorized service-role scheduler. - Confirm the AI result reconciler and 24-hour replay cleanup jobs are scheduled. When
pg_cronis unavailable, schedulecanvassing_reconcile_ai_requestsandcanvassing_run_ai_request_cleanupthrough an authorized service-role runner. - Confirm every organization has a valid IANA reporting timezone or the documented
UTCfallback. Verifycanvassing_get_user_goal_progressandcanvassing_get_reporting_period_boundsremain service-role-only, require the requested actor to be an active organization member, and return PostgreSQL-derived Today, Week, and Month boundaries for operational, XP, and balanced leaderboards. Exercise repeated and skipped local midnights, not only ordinary daylight-saving transitions. - Verify the filtered
canvassing_get_verified_leaderboardoverload remains service-role-only, checks the requested actor's active membership, rejects cross-organization or non-live campaign/territory scopes, and validates known XP event/source pairs against their live source records. Confirm the period-bounded attribution and sent-estimate plans use the migration's supporting indexes without reintroducing the redundant attribution entity index. Keep the previous four-argument overload available during the rolling application deployment. - Confirm authenticated roles cannot insert, update, or delete
user_xp_eventsoruser_gamification_profile, whileservice_roleretains explicit write grants. Run the authenticated-denial behavior regression as well as the grant inspection. Exercise signed negative reversal events and confirm period XP preserves the correction while balanced scoring gives it no positive contribution. - Confirm missing or disabled gamification settings return disabled, rank-only standings; a representative with rank-only privacy receives only their own row, while an authorized owner or manager retains administrative oversight.
- Confirm the manager-settings table constraint, service-only update function, shared TypeScript allowlist, mobile response validator, and native formatter all accept the same featured-metric set: balanced, XP, doors, contacts, qualified leads, appointments, estimates sent, sales, revenue, contact rate, appointment rate, and close rate. Confirm rate features render as percentages and only XP changes the main ranking away from balanced score.
- Verify
canvassing_get_representative_drill_targetsremains service-role-only and requires an active owner or manager actor plus an active organization representative. Confirm it rejects invalid or non-live campaign/territory scope, bounds date ranges and pagination, and applies representative, period, campaign, territory, disposition, and normalized-address filters before returning only target IDs, last activity, match counts, and a total. - Deploy the current proposal-delivery function after its database migration and secrets are ready.
- Run tenant-isolation checks with at least two organizations before enabling real field data.
- Verify authenticated limits are keyed to the authenticated organization and principal, not a caller-controlled IP, and that bulk imports consume weighted capacity.
- Verify manager team-location reads follow bounded cursor pagination and cannot expose another organization or an unauthorized representative.
- Verify active-member pricing, service, add-on, price-book, custom-template,
quote-core, estimate, proposal, job, lead-source, and organization reads do not cross tenants. Confirm authenticated clients cannot mutate residential proposals, organization rows, or quote-core records directly. Separately verify that the compatibility policies allow only commercial, fleet, and holiday-lights proposal inserts/updates and limit a sales representative to their own created_by rows.
- Exercise
sign_estimate_with_canonical_acceptancewith the service role and
confirm it atomically rejects a changed contract-only acceptance revision, total, pricing version, terms text, terms timestamp, organization, or existing signature. Confirm a viewed_at/view_count telemetry update does not advance the acceptance revision, and confirm authenticated clients cannot call the function directly.
- Verify mobile proposal sync recalculates canonical price and scope in one
transaction, requires the reviewed bundle revision for a sent proposal, and cannot let delivery claim a superseded proposal revision. Replay the same proposal, delivery, and client photo identifiers with both matching and conflicting payloads.
- Confirm new estimate-photo objects use the private
organization/estimate/client path policy, malformed legacy paths fail closed, authorized reads return only short-lived signed URLs, and the storage hardening migration's deferred legacy-row validation is completed only after the production-like backfill audit passes.
- Exercise the public web package, direct estimate, and maintenance canonical
acceptance flows against the current server-owned review. Verify staged package selection is not acceptance, exact ambiguous signature retries are idempotent, and a signed maintenance child can safely resume interrupted finalization without a second signature.
- Race an original post-sign request against an exact retry and confirm the
service-role-only reconciliation lease permits only one job/automation pass, an active claim reports that finalization is still in progress, and a stale claim can recover after the default 120-second lease window without creating a second job or rehash message. The database bounds an explicitly requested lease to 15–600 seconds; production currently uses the 120-second default.
- Accept an acceptance-owning estimate while forcing each secondary write to
fail in turn. Confirm the terminal estimate and its estimate-acceptance-effects:{estimateId} background job commit together, the minute worker authenticates internally, and retries eventually create exactly one accepted quote event, one webhook delivery per matching config, one Won transition, and one copy of every earned XP event. Confirm maintenance offers and residential package mirrors do not enqueue a second acceptance job, while linked fleet, commercial-building, and holiday-lights estimates do retain replay. Exercise retry exhaustion and verify queue metrics and dead-letter alerting are operational before enablement.
- Confirm accepted, signed, fingerprinted, snapshotted, and
maintenance-accepted estimates reject contract mutation, downgrade, and ordinary hard deletion at both the API and database layers. Verify the service-role-only legal-erasure RPC is unavailable to public, authenticated, and ordinary service-role DELETE writers, and that the checked sole-member organization-erasure path fails before deleting the Auth user if database cleanup does not complete.
Migrations are additive and should be reviewed before rollout. Do not reset the production database or the mobile WatermelonDB store to introduce canvassing.
2. Web Environment
Required application configuration includes:
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Authenticated web and API Supabase project |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Browser Supabase client |
SUPABASE_SERVICE_ROLE_KEY | Server-only scoped data operations |
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY | Authenticated web command-center map |
GOOGLE_MAPS_SERVER_API_KEY | Private server geocoding and cache population |
OPENAI_API_KEY | Canvassing AI provider access when AI is enabled |
NEXT_PUBLIC_APP_URL | Canonical customer and application link origin |
CUSTOMER_TOKEN_SECRET | Stable signing secret for customer estimate and proposal links |
The canvassing import path requires GOOGLE_MAPS_SERVER_API_KEY when an address needs geocoding. It does not fall back to a browser key. Keep the server key out of browser bundles and EAS environments.
Use an authorized GET /api/health/canvassing or HEAD /api/health/canvassing request as the feature-specific release gate:
curl --fail \
-H "Authorization: Bearer $CRON_SECRET" \
https://app.cleanestimate.pro/api/health/canvassingA 200 response proves that the private server geocoding key is present and that Supabase exposes every canvassing relation and RPC referenced by the web/mobile runtime plus the atomic mobile proposal handoff. A 503 response means canvassing must remain disabled even when the general /api/health endpoint is green. The route rejects unauthenticated requests before contacting Supabase, returns missing schema path names only to the authorized release monitor, and never returns credential values.
Proposal email/SMS also requires the existing delivery-provider secrets in the Supabase function environment, including the configured Resend and Twilio credentials for the channels your organization uses.
The Vercel ignored-build command must always return a build-required result for the production environment. Preview deployments may continue to skip mobile-only, documentation-only, migration-only, script-only, and test-only changes. This prevents a release-evidence commit at the tip of a multi-commit main push from masking earlier web-runtime changes in the same release.
Use real target-environment secrets for release verification. Placeholder Firebase files, example Supabase keys, unrestricted Maps keys, missing AI credentials, or test delivery credentials do not satisfy this gate. Confirm every secret by exercising its actual server, native, AI, geocoding, or delivery path without printing the value to logs.
3. Native Build Environment
All EAS profiles must point to the intended API origin and Supabase environment. Confirm:
EXPO_PUBLIC_APP_URLEXPO_PUBLIC_SUPABASE_URLEXPO_PUBLIC_SUPABASE_ANON_KEYEXPO_PUBLIC_EAS_PROJECT_IDEXPO_PUBLIC_GOOGLE_MAPS_API_KEYor the supported legacyEXPO_PUBLIC_GOOGLE_PLACES_API_KEYfor mobile address searchEXPO_PUBLIC_GOOGLE_MAPS_ANDROID_API_KEYGOOGLE_SERVICES_JSONor the equivalent EAS file secretAPP_ENV
Each development, preview, and production build profile selects its matching named EAS environment. The Supabase URL and anonymous key are intentionally absent from committed profile env blocks so a stale repository value cannot override the remote EAS environment. Before every build, confirm the selected remote Supabase URL matches the web application's target project.
Inspect the remote EAS environment for every release profile; a local .env or successful static export does not prove those remote values or file secrets exist. Verify the mobile Maps/Places key, Android Maps SDK key, and GOOGLE_SERVICES_JSON file secret resolve in the intended EAS project without printing their values.
The public mobile Maps/Places keys are client identifiers, not private server keys. Restrict them to only the APIs and application identities they require. Restrict EXPO_PUBLIC_GOOGLE_MAPS_ANDROID_API_KEY in Google Cloud to:
- Maps SDK for Android
- Android package
com.cleanestimate.pro - Every certificate fingerprint used by development, preview, and production signing as appropriate
The iOS app uses the supported native map provider and does not need the private server geocoding key.
The native config plugin must generate Android allowBackup="false" plus file, database, shared-preference, external-storage, cloud-backup, and device-transfer exclusions. The iOS application must mark its Documents directory as excluded from backup. Inspect the generated signed projects; source plugin tests alone do not prove the release binaries contain these controls.
4. Location and Notification Configuration
The native configuration must retain:
- iOS
UIBackgroundModeswithlocation - iOS When In Use and Always-and-When-In-Use location explanations
- Android fine, coarse, background-location, foreground-service, and foreground-service-location permissions
- Android foreground tracking notification support
- Android camera access for estimate photos, with image-picker microphone
access explicitly disabled because the field workflow does not record audio
- iOS supported background-location indicator behavior
Background location must be explained to the representative before the permission prompts. Tracking must begin only after the server starts an active shift and must stop when the shift ends, the user signs out, or the organization changes. If an attempted upload of a just-collected background batch receives an authentication or permanent failure, that batch must enter the scoped durable queue before tracking suspension and the interruption event. Context-change teardown must also preserve the departed organization/user's queue: if GPS or other canvassing work remains pending, or authentication blocks a flush, the shift-end write is queued behind that work while downloaded canvassing views are cleared. A different account or organization must not be able to read or process the quarantined queue.
Expo Go is not a valid background-location acceptance environment. Use a development, preview, or production native binary.
5. EAS and Store Credentials
Before creating or submitting production binaries:
- Confirm EAS remote iOS distribution certificates and provisioning profiles.
- Confirm the App Store Connect account, app record, bundle ID, agreements, and privacy answers.
- Confirm Google Play app signing, package record, background-location declaration, data-safety answers, and production track access.
- Supply the Google Play service-account file securely when using EAS submit.
mobile/google-service-account.jsonis intentionally ignored and must not be committed. - Confirm the Android app bundle and iOS archive use the intended production channel and runtime version.
- Confirm production map-key restrictions include the actual store signing certificate.
Store submission credentials are external prerequisites. A successful TypeScript check or Expo export does not validate them.
Privacy counsel or another authorized reviewer must approve the employee location notice, raw GPS retention choice, AI/coaching disclosures, proposal-delivery retention, and applicable labor/privacy terms. App Store and Google Play privacy, background-location, data-safety, and review approvals are separate release gates.
6. Automated Verification
Run the current repository checks from the web and mobile workspaces:
Web: lint, TypeScript, full tests, production build
Mobile: TypeScript, full Jest suite, Expo Doctor
Native bundle: Android export and iOS export
Docs: TypeScript and production buildAlso verify:
- Migration security and manager-workflow tests
- Campaign, territory, imports, reporting, exports, settings, AI, GPS, estimate acceptance, scheduling, and proposal-delivery tests
- Durable visit-claim tests for exact replay, conflicting fingerprints, active leases, stale recovery, CRM reuse, communication suppression, and manager-review evidence
- GPS batch tests for exact replay, immutable payload conflicts, wrong actors, malformed RPC summaries, and replay attempts after raw-location retention
- Visit-request retention tests for the 45-day cutoff, bounded cleanup, reserved-claim preservation, scheduler fallback, and health-state access
- RFC-style CSV tests for quoted multiline fields, escaped quotes, row/cell limits, duplicate headers, malformed quoting, and exact row widths
- Concurrent 12-meter deduplication tests across CSV, CRM, map entry, and field-created properties
- Incremental-rollup reconciliation tests against full refresh results
- Atomic AI quota tests for reservation, concurrency, exact replay, staged-result reconciliation, indeterminate provider outcomes, and replay cleanup
- Local proposal/canvassing scope isolation; sign-out, user-change, and organization-switch cache cleanup; scoped GPS and shift-end queue preservation and ordering; and native backup-exclusion tests
- Canonical residential bundle scope, digest, expiry, cache-fallback,
permission, rate-limit, catalog, add-on option, custom-template, and published-Suds tests
- Shared service-measurement mapping and Step 3 validation for every supported
residential service, including configured types and whole-window counts
- Residential sync tests for server recalculation, exact sent revision/total,
stale catalog and permission rejection, nonbillable incomplete drafts, and delivery dependency blocking
- Builder autosave tests for first-edit draft creation, debounce/background
flush, in-flight ordering, proposal-generation races, explicit-save reuse, and account isolation
- Organization API allowlist/logo-scope/rate-limit tests and pipeline
ownership, transition, acceptance-authority, and optimistic-concurrency tests
- Canonical acceptance fingerprint, signature service, public estimate/shared
quote/package/maintenance routes, mobile detail/accept route, exact retry, stale estimate/terms, package-stage ordering, unsigned-maintenance rejection, and PostgreSQL compare-and-set tests
- Transactional accepted-estimate outbox tests for first-transition enqueue,
tenant-bound internal authorization, maintenance/package-child exclusion, deterministic quote/webhook/XP replay, strict failure retry, and webhook conflict inference
- Private photo-path integrity, estimate/client binding, short-lived signed
reads, private stored-object deletion, in-flight upload cancellation, and fail-closed unknown-bucket tests
- Authenticated-principal rate-limit and team-location cursor-pagination tests
- Assigned-target property-context tests for organization/live scope, territory access, the independent Clients + Estimates and Clients + Jobs permission gates, and explicit unavailable states
- Fresh target reconciliation tests that project a server do-not-knock restriction across every downloaded target for the property without rolling back a newer unsynced visit
- Organization-timezone and PGlite regression tests for
UTCfallback, spring and fall daylight-saving transitions, repeated and skipped local midnight, exact-boundary requests, identical period bounds across operational, XP, and balanced leaderboards, and matching organization-local calendar edges across reports, property lists/details, representative drills, routes, exports, and Manager AI. Verify the web filter labels expose the IANA zone, reset uses the organization-local current date plus the prior 29 dates, and report headings and daily activity buckets use and label that same zone. Reject impossible Gregorian dates and explicit timestamps that omitZor a numeric UTC offset. - Filtered-leaderboard tests for authorized campaign and territory scope, Sales reps only, XP-or-balanced main ranking, independent emphasis of every supported count, currency, and rate metric, qualified-lead detail projection, top-25-plus-own bounding, a maximum 26-profile identity lookup, empty-result short-circuiting, signed XP reversals, source actor and event/source-pair validation, live-environment isolation, fail-closed privacy/settings behavior, and response PII minimization
- Representative-drill migration and route tests for service-only execution, owner/manager authorization, cross-organization and non-live scope rejection, representative/date/campaign/territory/disposition/address filtering, distinct door/visit/estimate/sale source semantics, exact
target_iddetail filtering, campaign/territory disambiguation for reusable properties, visit-cohort conversions, deterministic ordering, page bounds, malformed RPC rejection, organization-scoped target hydration, and focus-specific property-detail filtering - Mobile leaderboard tests for every required activity, conversion, revenue, and XP card field plus qualified leads; visit-cohort contact/appointment/close rates versus activity-period outcome counts and revenue; compact-to-full-screen scroll behavior; per-card featured-metric promotion and highlighting; open-only 120–150-second staggered polling; stable-principal rate limiting; a 30-second organization/rank/filter/reporting-bound score cache that reapplies authorization, privacy, and profile selection per request; private non-cacheable HTTP responses; exact version 4 device-cache provenance; self-only Current user persistence with no teammate identifiers or names; five-minute expiry and 30-second future-skew rejection; exact echoed filter and ordered-bound validation; rejection and writable-maintenance purge of legacy version 3 snapshots; authorization purges; disabled/rank-only purges; a failed-purge privacy barrier that blocks subsequent offline identity fallback; and malformed-response rejection
- No hard-coded service-role or AI provider keys are present in the mobile bundle
- The web and mobile API origin returns the expected authenticated response
- Live and test canvassing records remain separated
The final merged local release-candidate record on July 24 is:
- Web Vitest: 441 files / 2,052 tests
- Web TypeScript and production build: passed; 434 routes/pages
- Strict lint over every changed TypeScript/TSX file: 0 warnings / 0 errors
- Web production-only and full dependency audits: 0 vulnerabilities
- Mobile Jest: 60 suites / 402 tests
- Mobile TypeScript and Expo Doctor: passed; 19/19 Doctor checks
- Android/iOS static exports: passed; 4,403 / 4,377 modules and
9.3 / 9.2 MB Hermes bundles
- Clean Supabase PostgreSQL 17/PostGIS migration and seed: passed; database
lint reported 0 errors and the pgTAP authorization matrix passed 24/24 tests, including a 10,000-target set-based rollup fixture
- Docs TypeScript and production build: passed; 152 static pages
The latest completed mobile dependency audit in this release task reported no vulnerabilities. A final registry refresh could not complete because npm returned HTTP 503 on three attempts. These local results remain repository evidence only; static exports are not signed native builds, and the migration, load, authenticated-deployment, physical-device, store, legal, and privacy gates below remain open.
Accepted-estimate secondary effects now have a durable replay boundary. The first acceptance-owning estimate transition to accepted or signed queues a tenant-scoped internal job in the same transaction. Quote events, each configured webhook delivery, the Won transition, and XP ledger entries use deterministic keys, so the original request and a retry can overlap without duplicating them. Maintenance offers and residential package mirrors are excluded; linked fleet, commercial-building, and holiday-lights estimates retain replay. The release gate is now operational evidence that the scheduled background worker, internal secret, queue monitoring, retry progress, and dead-letter alerting work in the production-like target environment.
Run the dedicated read-only staging profile with independent temporary admin cookies and native bearer sessions:
npm run test:stress:canvassing -- \
--base-url=https://staging.cleanestimate.pro \
--cookie-file=.secrets/canvassing-admin-cookies.txt \
--mobile-session-file=.secrets/canvassing-mobile-sessions.json \
--campaign-ids=campaign-uuid \
--territory-ids=territory-uuid \
--representative-ids=representative-uuidNever commit the credential files. Give a native session "managerAccess": true only when the account really is an owner or manager; the manager-only team-location scenario is omitted for ordinary representatives. The default profile ramps through 10, 50, 150, and 250 paced sessions and requires at most 1% errors, at most 2% throttling, 1200ms overall p95, 1500ms authenticated-route p95, and at least 20 samples per included authenticated scenario. Supply at least half as many independent principals as peak sessions so account-level protection is not mistaken for system capacity.
A bounded local proof against 10,000 assigned targets completed 65/65 health and representative-native requests with 0% errors, 0% throttling, and 201ms p95. It validates the harness and local read path, not production capacity. After the full staging migration, also exercise simultaneous imports/manual field additions, visit and CRM retries, GPS batches and retention, command-center reporting/rollups, paginated representative source drills, paged team locations, proposal delivery, and AI reservation/reconciliation. Record latency, errors, duplicates, lock waits, backlog, database connections, CPU/IO, index growth, and dead tuples. The deterministic 1,000-participant leaderboard regression and the local proof are not substitutes for this target-environment run.
An export verifies JavaScript bundle creation. It does not compile store-native projects, validate signing, prove remote EAS Maps/Places or GOOGLE_SERVICES_JSON configuration, or prove locked-screen GPS behavior.
The July 24 release audit found and removed stale committed Supabase overrides from every EAS build profile. The named remote development, preview, and production environments point at the same Supabase project used by the web deployment. The mobile Maps/Places and Android Maps variables are now present in all three remote EAS environments and match the live estimator Maps credential. Android internal build 45 completed successfully from merged main commit 18a14345 with those variables and the live Supabase configuration. Native emulator acceptance also verified the map, disposition sheet, address list, status chips, territory selector, and active-shift controls. The territory selector is now height-bounded so it cannot squeeze the field map, and the active-shift controls remain elevated above the native map surface on Android.
The final Google Cloud application restriction still needs a signed-device check against package com.cleanestimate.pro and the active EAS signing certificate. GOOGLE_SERVICES_JSON also remains absent: every local file found during the audit identifies itself as a placeholder and must not be promoted as production Firebase configuration. The first iOS internal build remains blocked until an interactive Apple signing session creates credentials suitable for internal distribution. A current signed Android physical-device pass, real Firebase configuration, and the first signed iOS/TestFlight build therefore remain launch gates.
7. Physical-Device Acceptance
Complete and sign this matrix on at least one current physical Android phone and one current physical iPhone using a release-signed preview or production-candidate build:
- Sign in to the intended production-like organization.
- Download an assigned territory and verify map and list paging.
- Start a shift and grant precise foreground and background access.
- Confirm Android shows the persistent tracking notification and iOS shows supported location activity.
- Lock the phone and leave the app backgrounded long enough to collect route points.
- Turn off cellular and Wi-Fi.
- Long-press a missing door, confirm its pending offline marker cannot record an outcome, and later verify private address resolution.
- Open a known property and verify distance, current disposition, attempts, do-not-knock state, CRM warning, recent estimates, and prior services. Repeat with a role that has canvassing access but lacks the underlying estimate/job permissions; confirm those sections say they are unavailable rather than showing data or a false empty history. Reopen the allowed property offline and confirm the history is visibly cached.
- Record multiple door outcomes, including a required note and follow-up.
- Confirm Interested or Appointment Set, then immediately start an estimate. Verify the newly returned client/lead identifiers and entered name, phone, and email are prefilled and remain linked.
- Mark a reusable property Do Not Knock while offline and confirm every downloaded target for that property is blocked immediately, including a target in another campaign. Restore service and verify the organization-wide server state. From a second signed-in device with a stale target page, open that property and confirm the fresh context blocks every downloaded sibling target before another normal outcome can be saved.
- Create a local residential estimate while offline. Verify the first
meaningful edit autosaves, every selected service requires its real measurement/type, the cached bundle is visibly scoped and current, and no customer signature can be captured from the local-only proposal.
- Restore connectivity and verify property, visit, CRM result, and proposal
sync once without duplicates. Reopen the canonical server estimate and confirm the customer sees the exact scope, total, pricing version, and current organization terms before signing. Change the estimate or terms after opening the sheet and confirm the stale signature is rejected and a fresh sheet clears the old signature/consent. Retry an intentionally ambiguous save with the identical PNG, signer, and timestamp and confirm idempotent success. Select each package and confirm selection only stages a fresh canonical review; acceptance and automation must wait for the matching signature. Turn off a service or add a note and confirm the page requests a revised estimate rather than accepting a browser-calculated total. Confirm maintenance acceptance cannot complete without its matching signature. Also confirm exact GPS replay succeeds once and a deliberately changed replay of the same GPS identifier is rejected.
- Simulate a lost visit response, a concurrent retry, and a stale recovery. Verify one visit/CRM result, retry-window behavior, suppressed recovery communications, and manager-review evidence.
- Use a quick-estimate template and verify it prefills approved values without bypassing pricing or Red Line controls.
- Open Goals & team standings and verify the compact field-screen control expands to a full-screen scrollable panel. Exercise Today, Week, Month, authorized campaign, authorized territory, and Sales reps only filters. Confirm each card shows rank, representative, verified doors, contacts, qualified leads, appointments, estimates sent, sales, revenue, contact rate, appointment rate, close rate, and XP. Exercise every featured-metric choice, confirm counts, currency, and rates are formatted correctly, and confirm the selected value is promoted and highlighted on every card. Confirm the top 25 plus the current user's own row; confirm XP ranks by signed XP while every non-XP featured metric retains balanced main ranking; and confirm polling occurs only while the panel is open at the staggered 120–150-second interval.
- Verify server-derived goal progress, operational standings, signed XP standings, balanced standings, disabled settings, rank-only privacy, and manager oversight on both sides of an organization-local midnight and a daylight-saving boundary. While online, confirm full standings can show the top 25 plus own row. Then go offline and confirm version 4 displays only the signed-in representative under Current user, includes no teammate identifiers or names, shows its download time, and expires within five minutes. Reject a snapshot with mismatched echoed filters or bounds, a download time more than 30 seconds in the future, and a legacy version 3 identity-bearing row; confirm writable cache maintenance removes the legacy row. Force the local purge to fail after a rank-only or disabled response, then go offline and confirm the in-memory privacy barrier does not reopen stale identities.
- In the web Reporting leaderboard, set campaign, territory, disposition, address, and organization-local date filters, then select a representative name and the linked Verified doors, Estimates, and Sales values. Confirm each focus returns only the exact verified source targets for that representative and preserved filter scope, paginates without duplicates or omissions, and shows the correct Source representative, per-target match count, Last matching activity, campaign, and territory. Use a reusable property represented by more than one target and verify the labels disambiguate it. Open Details and confirm the request carries the exact
target_idplus representative, period, campaign, territory, and disposition scope and limits estimate/sale records to the selected focus. Record a later outcome and verify it updates the original verified-visit cohort conversion without being reassigned to another target. Repeat with a non-manager and a cross-organization scope and confirm access fails closed. - Send and resend to controlled email/SMS recipients and verify the delivery log.
- Schedule a follow-up, appointment, and accepted-estimate work action.
- End the shift and verify native tracking stops immediately.
- Keep the app locked/backgrounded after ending and confirm no new location points arrive.
- Revoke location permission mid-shift and confirm the interruption warning, notification, manager state, integrity evidence, and recovery behavior.
- Force a background GPS upload to return an authentication failure, and separately exercise a controlled permanent rejection. Confirm the just-collected batch enters the original organization/user's queue before the interruption event and native tracking suspension, then verify retry recovery or Needs review behavior as appropriate.
- With GPS or another canvassing write still pending, sign out, change users, or switch organizations during an active shift. Confirm native tracking stops immediately, downloaded canvassing views for the departed scope are cleared, the scoped GPS work is not deleted, and the shift end is queued behind the earlier work when the flush is pending or blocked by authentication. Confirm another account or organization cannot read or process that queue, then return as the original account in the original organization and verify the queue can recover in dependency order.
- Capture, sync, view, and delete an estimate photo. Confirm customer and PDF
reads use expiring signed access, a queued deletion cancels the upload, and deleting a synced photo removes the private stored object without exposing another estimate or client.
- Inspect the installed build and backup behavior to confirm Android and iOS sensitive-storage exclusions survived native generation and signing. Confirm the Android manifest does not request microphone access.
Record the device, OS version, build profile, build identifier, permission states, timestamps, and observed server records. Simulator and automated tests do not replace this evidence.
8. Pilot and Enablement
Start with a small internal territory and controlled recipients.
- Review the employee location notice and raw GPS retention setting.
- Confirm leaderboard privacy and private coaching access.
- Validate disposition requirements and quick estimate templates.
- Confirm manager escalation for failed offline items.
- Review recovered-visit communication suppression and every automatic integrity-signal category with managers.
- Reconcile visits, appointments, estimates, signatures, jobs, revenue, commission, and XP attribution.
- Review API, Edge Function, geocoding, AI, email, SMS, and rate-limit logs.
After the pilot passes, the organization owner can open Canvassing and select Enable Canvassing.
Final Release Gates
Do not describe the release as live until all of the following evidence is attached to the release record:
- Real production secrets and restricted signing/key configuration have been verified through their actual paths.
GET /api/health/canvassingreturns200in production; an authorized
release-monitor request reports no missing schema paths.
- The complete migration chain has applied successfully to an upgraded production-snapshot PostgreSQL/PostGIS staging database, with RLS and service-role boundaries tested.
- The production-like load/concurrency test meets its predeclared thresholds.
- Signed Android and iOS physical-device tests cover online, offline, locked/background GPS, retries, stale recovery, scope changes, and backup exclusions.
- Privacy/labor counsel or another authorized reviewer has approved the notices and retention choices.
- App Store and Google Play credentials, disclosures, background-location review, and required approvals are complete.
Current Release Status
The July 24 production database rollout is complete. Before the write, the release operator captured the public, auth, storage, and supabase_migrations schemas plus every table's production rows in a hashed recovery manifest. The exact 46-migration backlog then passed twice against the live PostgreSQL 17 schema and data inside rollback-only transactions, including the migration-ledger writes, before the same transaction body committed.
Post-apply evidence now shows:
- The Supabase migration ledger is 224/224 through
20260723269000_service_role_runtime_grants.sql.
- The protected production readiness check sees all 44/44 required
relations and RPCs, with zero missing paths.
- All 19 canvassing tables have RLS enabled.
- All 74 canvassing/mobile proposal routines retain
service_roleexecution,
and every canvassing table retains the required trusted-server DML grants.
- A database-owner-free smoke using the
authenticatedrole and a real active
organization member can read the campaign relation through RLS.
- No temporary backup or diagnostic role remains in production.
Vercel production now contains GOOGLE_MAPS_SERVER_API_KEY as a sensitive variable, backed by the Google Maps credential already used by estimator address lookup. That credential passed a server-side forward-geocoding smoke before the alias was added, and deployment dpl_AwitmmHL3VNGG9Csfzzhajao4nRA completed in the READY state and was aliased to cleanestimate.pro.
This removes the missing-variable blocker, but it does not prove the final Maps restriction model: the current value is also shipped to the estimator browser through NEXT_PUBLIC_GOOGLE_MAPS_API_KEY. Before production enablement, replace or verify it as a separately restricted server credential, run the authorized readiness request until it returns 200, and complete the remaining native, load, physical-device, store, and legal gates above.
Supabase's passwordless CLI migration-list command still fails while the platform initializes its PostgreSQL 17 cli_login_postgres role. This does not affect Auth, PostgREST, RLS, service-role APIs, or the application runtime. Use the protected readiness endpoint and the migration ledger for release evidence until Supabase repairs that platform-side CLI route; do not add a persistent diagnostic login role as a workaround.
Command-center setup message
If the command center reports Canvassing setup incomplete, the application has detected that a required canvassing table, column, or RPC is not available in the connected Supabase schema. This is a deployment configuration problem, not a campaign permission denial. The command center stays offline, returns HTTP 503 with code CANVASSING_SETUP_INCOMPLETE, and does not retry queued native writes or change campaign data.
Release operators should:
- Keep canvassing disabled for customer organizations.
- Run the protected
GET /api/health/canvassingcheck and record every
missing schema path.
- Apply and validate the complete migration chain in the production-snapshot
staging environment before production.
- Confirm the protected readiness response is
200before enabling the
command center or issuing new signed native builds.
For the current July 24 rollout, steps 2 and the production database portion of step 3 are complete: the monitor reports zero missing schema paths and the production ledger is current. Step 4 remains blocked only by the private server geocoding key and the other non-database release gates above.
Do not troubleshoot this message by broadening organization permissions or assigning more territories. Those changes cannot repair a missing database contract.
Documentation may be published before the application rollout so the launch team can follow this checklist. Publication of this page is not a claim that the production migration, web deployment, EAS build, App Store release, Google Play release, legal/store approvals, load test, or physical-device acceptance has occurred.
Related articles
Was this article helpful?
Still need help? Contact support