This is the honest roadmap. It supersedes the earlier "all phases ✅" status. The previous markings did not reflect the state of the code (the native bridge had placeholder branches, several config keys were parsed and discarded, the Gradle build was wired to a Windows-specific path, etc.). The list below is what is actually in the repo and what is next.
- Repo hygiene: removed leaked
blokzFirebase clients fromgoogle-services.json; deleted deadMainActivityduplicates and the unrelated 37KBGEMINI.mdAI-agent prompt; consolidated platform Kotlin under one canonical package; tightened lint rules (analysis_options.yaml). - Gradle: rewrote
android/app/build.gradle.ktsin proper Kotlin DSL (was Groovy syntax inside a.ktsfile → would not compile); replaced Windows-onlyC:/dev/flutter/...path insettings.gradle.ktswithlocal.propertieslookup; added explicit dependency declarations (AppCompat, CameraX, ML Kit, UMP, Play Core, Browser Custom Tabs); safe optional signing config that falls back to debug signing whenkey.propertiesis missing. - Android polish: ProGuard rules, network security config (HTTPS-only base
with dev cleartext template), data extraction rules (no backups by
default), tightened FileProvider paths, default notification channel
meta-data, explicit
<queries>for Android 11+ visibility, AdMob app id meta-data placeholder, deep-link<intent-filter android:autoVerify>template.
MainActivitymethod-channel handler now covers every contract method:gatherConsent,scanBarcode(launchesScannerActivity, routes the result viaonActivityResult,E_BUSYguard against concurrent scans),downloadBlob(Base64 →MediaStore.Downloadson API 29+, legacy Downloads dir +addCompletedDownloadon API ≤ 28),registerHttpDownload(DownloadManager enqueue), file uploads throughWebSightChromeClient.- Dart
JsBridge: stable error codes (E_PERMISSION,E_CANCELED,E_ARGS,E_INTERNAL,E_ORIGIN,E_UNSUPPORTED); runtime origin enforcement (drops calls whencurrentUrl().hostis not insecurity.restrict_to_hosts); JSON-encoded callback id interpolation (no raw quote injection); realdevice_info_plusvalues; structured error payloads. assets/websight.js: Promise contract returns{code,message}on reject; monotonic callback id; defensivepostMessagewhen channel missing.WebSightMessagingServicefor FCM (silent payload pass-through, default channel creation, route-extra propagation for push-tap deep linking).
lib/config/feature_configs.dart: hand-rolled feature configs for splash, offline HTML, custom user scripts, user agent modes, file uploads, downloads, billing, rating prompt, FAB, bottom tabs, drawer header/items, error pages. Avoids forcing abuild_runnerregenerate every time a new YAML key is added.lib/shell/action_dispatcher.dart: parses YAML action strings —navigate:/path,webview.reload,webview.back,bridge.<method>,store.rate,noop— used by AppBar / drawer / FAB / inbound events.app_shell.dart: AppBar actions, drawer (with header + items + footer), bottom navigation, and FAB are all driven from config.app_router.dart: initial location resolves fromapp.home_urlagainst the route table; parameterized routes work end-to-end (YAML/web/item/{id}→ go_router/web/item/:idwith{id}substituted into the page URL).
- Splash overlay (
splash.enabled,splash.timeout_ms). - Offline overlay with retry (
behavior_overrides.error_pages,offline_local_html). - Custom CSS / JS injection on page-finish from
webview_settings.custom_user_scripts. - User-agent modes:
system/append/custom. WebsightWebViewController.loadOfflineFallback()for the bundledassets/offline/index.html.
permissions_controller.dartfinally honorsnotifications.post_notifications_permission.fcm_controller.dart: foreground listener, token refresh stream, cold-startgetInitialMessage. Initializes only whennotifications.fcm_enabledis true.billing_controller.dart:in_app_purchasewrapper aroundbilling.product_ids(refresh / buy / restore / purchase stream).rating_controller.dart: launch counter viashared_preferences, firesin_app_review.requestReview()oncerating_prompt.after_launchesis reached.
- Unit tests for
feature_configs.dart,helpers.dart, andaction_dispatcher.dart. - GitHub Actions workflow runs format check,
flutter analyze,flutter test --coverage, and a debug Android build on every PR.
- Replaced four hardcoded native screen stubs (which shipped with mock
data:
$12,345.67, fabricated ticker rows, unwired settings toggles) withConfigurableNativeScreen. Settings variant readsWebSightConfig/WebSightFeaturesvia Provider and shows the real app identity, theme, analytics flags, FCM token, and IAP product count. Privacy / Terms / About routes are surfaced automatically when present in the route table. Other/native/*routes render a clearly labeled placeholder with the route path so it's obvious where to plug in real screens.
WebSightBridge.registerHttpDownload(url, opts?)exposesMainActivity.registerHttpDownload(DownloadManager) to JS._installDownloadInterceptor()runs on every page-finish (idempotent per page) whendownloads.enabledanddownloads.use_android_download_managerare both true. It catches clicks on<a download>and on links whose href ends in a downloadable extension, routes HTTP/S toregisterHttpDownloadand blob: todownloadBlob. Modifier-clicks are left to the browser.
SplashFeatureextended withimageAsset,backgroundColor,tagline,fadeOutMs. Asset paths are normalized so users can writesplash/logo.pngorassets/splash/logo.png._SplashOverlayrenders the optional logo + tagline over the configured background (or theme surface), auto-picks a contrasting foreground viaThemeData.estimateBrightnessForColor, and cross-fades on exit.- Native pre-Flutter splash handed off to
flutter_native_splash(added as dev dep with a starter pubspec block andassets/splash/directory). README documents the setup + regeneration command.
docs/WHITELABEL.md— end-to-end recipe for spinning the template up against any web app's domain. Covers identity, icon generation (icon.kitchen →flutter_launcher_icons), splash, Firebase / AdMob / IAP, signing, smoke checklist, Play Console listing, and trademark / ToS / financial-services policy guidance.examples/directory — first entryblockchair.yamlis a complete drop-in for the third-party-site wrapper case.- New
legal:YAML block +DisclaimerGatewidget for opt-in first-launch unofficial-app disclaimers. Acceptance is hash-keyed on the body text so edits re-prompt automatically.
dart run tool/init.dart— interactive wizard with rich TUI (mason_logger) and plain-prompt modes; user picks at start. Writes the YAML, runs the propagator, offers icons / splash / Firebase / keystore / smoke. Resumable.dart run tool/doctor.dart— pass/warn/fail report covering toolchain (Flutter, JDK 17), project identity, Firebase wiring, launcher icon, splash image, signing, AdMob App ID (test vs real), deep-link host vs YAML, leftover placeholders. Exits 1 on hard fail so it can gate CI.tool/init/directory split for SRP: prompter.dart (interface + plain + rich impls), runner.dart (step orchestration), init.dart (CLI wrapper).
- Server-side IAP receipt validation reference: not in scope for v1.
Documented as integrator responsibility; receipts arrive in
BillingController.purchases. - Honest README rewrite: capture the new state, drop the overclaims.
CHANGELOG.mdentry for v1.0.
- iOS support (WKWebView shell, ATT consent, App Store metadata, signing).
- Single-binary remote-config variant (signed config download with TOFU + rotation).
- CLI scaffolder:
dart run websight new --config foo.yamlproduces a ready-to-build project. - Server-side IAP validation reference impl (Cloud Functions + Play Developer API).
- WebView download auto-detection.
- Material You dynamic theming.
- Interstitial / rewarded ads.
- Expanded JS bridge: biometric auth, geolocation, contacts, haptics.
- AI contextual chat (Gemini API): page-aware in-app assistant.
- AI app factory: agent that scaffolds a WebSight project from a URL.
Each section above maps to commits on the claude/project-review-v1-roadmap-Wi8H8
branch. The internal plan lives at /root/.claude/plans/ (developer
machine only) and at docs/internal/config-reference.yaml for the
canonical YAML schema.