Recycling is confusing not because people don’t care, and not because municipalities aren’t trying — but because the system is inherently complex. Rules vary by city, by hauler, by material, by housing type, and by market conditions that change faster than public guidance can realistically keep up.
People are expected to make correct, context-dependent decisions in real time, often with incomplete or outdated information. Municipal recycling programs know this tension well.
RePath Mobile exists to reduce that friction.
This app is an offline-capable guide that helps people decide what to do with the thing in their hand right now: reuse it, give it away, sell it, recycle it, take it to a drop-off location, or trash it as a last resort — based on local rules and real-world constraints.
I am building RePath Mobile because:
- Recycling is an information-routing problem, not an education or motivation problem
- “Is this recyclable?” is the wrong question, but it’s the question people have
- Clear, ranked options reduce contamination more effectively than yes/no answers
- Reuse, resale, and proper disposal are as important as recycling itself
- Tools that lower cognitive load outperform tools that rely on perfect behavior
This repository contains a thin mobile client that consumes RePath Core decision logic and municipal data packs. It is designed to complement — not replace — existing municipal guidance by making that guidance more accessible at the moment decisions are made.
For municipalities and recycling professionals, RePath is intended to be a force multiplier:
- a way to extend local rules into people’s pockets
- a mechanism to reduce wishcycling without increasing enforcement or outreach burden
- a shared, updatable interface between complex programs and everyday behavior
RePath is not about assigning blame. It’s about acknowledging complexity — and giving people a better path through it.
Minimal React Native (Expo) prototype that:
- asks for location (or ZIP)
- loads a municipality pack
- renders ranked pathways (reuse/repair/donate/recycle/trash)
- shows channel-based reuse options (marketplaces + community groups)
- shows donation places when available in the pack
- asks follow-up city/ZIP questions only when required to resolve top channel links
- falls back to nationwide U.S. guidance for unknown valid ZIPs, with an in-app accuracy warning
- uses a bundled search index (
assets/packs/search.json) for lookup - supports camera detection (YOLOv8 TFLite, single-shot frame processor for POC stability)
This is a thin proof-of-concept. Replace bundled packs with remote manifest + cached downloads.
Run tests locally:
npm test
npm run test:coveragenpm test discovers suites in this order:
test/unit/**/*.unit.cjstest/integration/**/*.integration.cjstest/acceptance/**/*.acceptance.cjs
Notes:
- Root-level
test/*.test.cjsfiles are not executed by the current runner. - For pull requests, CI publishes coverage summary/artifacts and posts coverage delta comments.
- Set
CODECOV_TOKENin GitHub Actions secrets (required for private repositories).
Role contracts are defined in:
docs/agents/README.mddocs/agents/ux.mddocs/agents/qa.mddocs/agents/devsecops.mddocs/agents/pm.md
Run local review checks:
npm run smoke
npm test
npm run review:devsecops
npm run review:pmPull request flow:
- Fill
.github/pull_request_template.md, including theAgent Reviewssection. - Open a PR to trigger
.github/workflows/role-review-gates.yml. - You can also run
Role Review Gatesmanually from GitHub Actions (workflow_dispatch).
Gate configuration:
REPATH_AUDIT_FAIL_LEVEL(default:critical) controls thenpm auditthreshold. Supported values:off,low,moderate,high,critical.REPATH_PM_STRICT(GitHub repository variable) makes PM contract checks blocking when set totrue.
Release notes source-of-truth:
release-notes.md
Android artifact build (local):
npm run release:android -- --tag vX.Y.ZAndroid artifact build + GitHub release upload:
npm run release:android:publish -- --tag vX.Y.ZThe release script produces:
app-release-vX.Y.Z.apkapp-release-vX.Y.Z.apk.sha256app-release-vX.Y.Z.aabapp-release-vX.Y.Z.aab.sha256output-metadata-release-vX.Y.Z.json
The release command also verifies:
- checksums match generated artifacts
- APK contains
assets/index.android.bundle - metadata
versionName/versionCodematch the tag
Debug build command is still available for local Metro-connected testing:
npm run release:android:debug -- --tag vX.Y.ZProduction signing inputs (environment variables):
REPATH_UPLOAD_STORE_FILEREPATH_UPLOAD_STORE_PASSWORDREPATH_UPLOAD_KEY_ALIASREPATH_UPLOAD_KEY_PASSWORD
For local non-production testing only, you can bypass signing enforcement with:
npm run release:android -- --tag vX.Y.Z --allow-debug-signingManual verification for an already-built release directory:
npm run verify:release:android -- --tag vX.Y.ZOptional CI guardrail:
- Run the
Android Release Guardrailsworkflow manually to validate release packaging on GitHub-hosted runners.
See docs/release-contract.md for artifact contract details.
The scan flow uses VisionCamera frame processors with a YOLOv8 TFLite model for single-shot detection (POC), then maps labels to pack items via the bundled search index.
Requirements:
- Development build (VisionCamera + TFLite are native modules; not supported in Expo Go).
- Pull a released model from
krispeterson/repath-model:npm run pull:model:release
- Pinned/default source is configured in
assets/models/model-release.json. - Model training/export lives in
repath-model(not this repo).
- Pinned/default source is configured in
- Detection boxes are hidden by default. For local debugging, set:
EXPO_PUBLIC_SHOW_DETECTION_BOXES=1
Dependencies used:
react-native-vision-camerareact-native-fast-tflitereact-native-worklets-core
Install guidance (network required):
npm install
-
Install project dependencies:
npm install
-
Pull the currently configured model release:
npm run pull:model:release
This verifies model checksums against the release manifest by default.
-
Create a development build (required for VisionCamera + TFLite):
npm run prebuild npm run android
Note: This POC uses
Frame.toArrayBuffer()for resizing, which requires AndroidminSdkVersion26+ (set via theexpo-build-propertiesplugin inapp.json).npm run prebuildgeneratesandroid/andios/from Expo and then applies a small patch that only bumps Gradle/AGP/Kotlin if the generated versions are below the minimums we need. This keeps the workflow reproducible even when the generated template versions change, without overriding newer versions. Minimums checked: Gradle 8.6, AGP 8.4.2, Kotlin 1.9.24.The POC uses a simple JS resize in the frame processor (pixelFormat
rgb) to avoid extra native dependencies. This is slower than the resize plugin but more stable for a prototype.Performance note: this is CPU-heavy and throttled to ~1 FPS for stability. If we move beyond POC, we should swap to a dedicated native resize plugin or a GPU-backed preprocessing path. This builds a dev client that includes native modules. Expo Go will not work.
-
Start the dev server:
npm run start
Notes:
- The camera pipeline requires a development build (custom native modules). Expo Go will not work.
android/andios/are generated by Expo prebuild and are intentionally ignored. If you upgrade Expo/RN, re-runnpm run prebuildand confirm the patch script still applies cleanly.- Model binaries in
assets/models/are local files. Labels/config metadata are tracked. Seeassets/models/README.mdfor model release integration details.
Model workspace note:
- Training/evaluation/release workflows are now owned by
repath-model. - In this repo, run
npm run ml:workspacefor handoff instructions. - Separation roadmap:
docs/model-repo-separation-plan.md.
-
Expo Go shows a blank camera / scan button fails
- Use a development build; VisionCamera + TFLite are native modules and do not run in Expo Go.
-
Model not loadederror- Ensure
assets/models/yolo-repath.tfliteexists and is not empty. - Restart Metro after adding the model so it gets bundled.
- Ensure
-
Poor detection accuracy
- Confirm your labels in
yolo-repath.labels.jsonmatch the model classes. - Verify the model input size in
src/App.jsmatches your exported model size.
- Confirm your labels in
-
Metro doesn't bundle .tflite
- Ensure
metro.config.jsincludestfliteinassetExts.
- Ensure