feat: design-first magic_example (full-set app + DESIGN.md + /preview catalog + agent infra) - #1
Merged
Merged
Conversation
… catalog + agent infra) Ready-to-start reference app for the fluttersdk ecosystem: - Full set installed: magic, magic_starter, dusk, telescope, magic_deeplink, magic_notifications, magic_social_auth, on fluttersdk_artisan; a Laravel api/v1 backend (magic-starter-laravel) under backend/. - DESIGN.md is the single source of truth; design:sync generates the wind theme (lib/config/wind_theme.g.dart), consumed in main.dart. - Dev-only /preview catalog (magic_devtools) listing foundations, the component library, and feature-screen previews driven by a backend-free mock harness; stripped from release builds. - Design-first agent infra: CLAUDE.md + AGENTS.md + .claude/rules + 3 skills + a component-visual-reviewer agent + docs/design-culture + component-registry. Verified locally end to end: flutter analyze 0, the app boots and renders (login/dashboard/profile + the catalog) in light and dark, the real backend api/v1/auth/login returns a Sanctum token, and release builds tree-shake the catalog.
There was a problem hiding this comment.
Pull request overview
Brings up a design-first magic_example reference application, including Flutter multi-platform runner scaffolding, a dev-only /preview component catalog with mock harness, DESIGN.md-driven Wind theme generation, and a Laravel backend/ skeleton intended to support api/v1 and Magic Starter features.
Changes:
- Adds platform runner scaffolding for Android/iOS/macOS/Linux/Windows/Web, plus basic smoke tests.
- Introduces Magic app bootstrapping/config (providers, routes, middleware) and a debug-only preview catalog with generated registry.
- Adds a Laravel backend skeleton with Magic Starter-oriented models, policies, and migrations, plus frontend build tooling (Vite/Tailwind).
Reviewed changes
Copilot reviewed 230 out of 277 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| windows/runner/win32_window.h | Adds Win32 window abstraction header |
| windows/runner/utils.h | Declares Windows runner utilities |
| windows/runner/utils.cpp | Implements console + argv UTF conversion |
| windows/runner/Runner.rc | Adds Windows resources/version/icon |
| windows/runner/runner.exe.manifest | Enables PerMonitorV2 DPI awareness |
| windows/runner/resource.h | Defines Windows resource IDs |
| windows/runner/main.cpp | Windows runner entrypoint wiring |
| windows/runner/flutter_window.h | Declares Flutter-hosting Win32 window |
| windows/runner/flutter_window.cpp | Implements Flutter window lifecycle |
| windows/runner/CMakeLists.txt | Windows runner build target setup |
| windows/flutter/generated_plugins.cmake | Generated Windows plugin wiring |
| windows/flutter/generated_plugin_registrant.h | Generated Windows plugin registrant header |
| windows/flutter/generated_plugin_registrant.cc | Generated Windows plugin registrant impl |
| windows/flutter/CMakeLists.txt | Flutter Windows build integration |
| windows/CMakeLists.txt | Windows top-level CMake wiring/install |
| windows/.gitignore | Ignores Windows ephemeral/build artifacts |
| web/manifest.json | Web app manifest for Flutter web |
| web/index.html | Web host page for Flutter web |
| test/widget_test.dart | Adds Flutter widget smoke test |
| macos/RunnerTests/RunnerTests.swift | Adds macOS test target stub |
| macos/Runner/Release.entitlements | macOS release entitlements |
| macos/Runner/MainFlutterWindow.swift | macOS window bootstrapping |
| macos/Runner/Info.plist | macOS app plist metadata |
| macos/Runner/DebugProfile.entitlements | macOS debug entitlements |
| macos/Runner/Configs/Warnings.xcconfig | macOS warning flags config |
| macos/Runner/Configs/Release.xcconfig | macOS release xcconfig includes |
| macos/Runner/Configs/Debug.xcconfig | macOS debug xcconfig includes |
| macos/Runner/Configs/AppInfo.xcconfig | macOS app name/bundle id config |
| macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json | macOS app icon asset catalog |
| macos/Runner/AppDelegate.swift | macOS app delegate behaviors |
| macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved | macOS SPM lockfile |
| macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist | macOS workspace checks |
| macos/Runner.xcworkspace/contents.xcworkspacedata | macOS workspace definition |
| macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme | macOS scheme configuration |
| macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | macOS project workspace SPM lockfile |
| macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist | macOS project workspace checks |
| macos/Flutter/GeneratedPluginRegistrant.swift | Generated macOS plugin registrant |
| macos/Flutter/Flutter-Release.xcconfig | macOS Flutter release include |
| macos/Flutter/Flutter-Debug.xcconfig | macOS Flutter debug include |
| macos/.gitignore | Ignores macOS ephemeral/Pods/user data |
| linux/runner/my_application.h | Linux GTK runner application header |
| linux/runner/main.cc | Linux runner main entrypoint |
| linux/runner/CMakeLists.txt | Linux runner build wiring |
| linux/flutter/generated_plugins.cmake | Generated Linux plugin wiring |
| linux/flutter/generated_plugin_registrant.h | Generated Linux plugin registrant header |
| linux/flutter/generated_plugin_registrant.cc | Generated Linux plugin registrant impl |
| linux/flutter/CMakeLists.txt | Flutter Linux build integration |
| linux/.gitignore | Ignores Linux ephemeral artifacts |
| lib/routes/app.dart | Registers app routes (dashboard) |
| lib/preview/teams_screen.preview.dart | Adds teams screen preview wrapper |
| lib/preview/settings_screen.preview.dart | Adds settings screen preview wrapper |
| lib/preview/screen_preview_scaffold.dart | Adds shared preview scaffold/harness mounting |
| lib/preview/register_screen.preview.dart | Adds register screen preview wrapper |
| lib/preview/profile_screen.preview.dart | Adds profile screen preview wrapper |
| lib/preview/login_screen.preview.dart | Adds login screen preview wrapper |
| lib/preview/dashboard_screen.preview.dart | Adds dashboard screen preview wrapper |
| lib/preview/_previews.g.dart | Generated preview registry list |
| lib/main.dart | Boots Magic app + devtool integrations + theme |
| lib/config/wind_theme.g.dart | Generated Wind theme + aliases from DESIGN.md |
| lib/config/view.dart | UI config for dialogs/confirms |
| lib/config/routing.dart | Routing/url strategy config |
| lib/config/network.dart | HTTP/network driver config |
| lib/config/magic_starter.dart | Magic Starter feature/routes toggles |
| lib/config/logging.dart | Logging channel configuration |
| lib/config/deeplink.dart | Deeplink configuration |
| lib/config/database.dart | Database connection configuration |
| lib/config/cache.dart | Cache driver/ttl configuration |
| lib/config/broadcasting.dart | Broadcasting connections configuration |
| lib/config/auth.dart | Auth guard/token/endpoint config |
| lib/config/app.dart | App providers + plugin service providers |
| lib/app/providers/route_service_provider.dart | Boots kernel/routes + debug preview routes |
| lib/app/providers/app_service_provider.dart | Boots Magic Starter user/nav/logout/team hooks |
| lib/app/middleware/redirect_if_authenticated.dart | Guest-only redirect middleware |
| lib/app/middleware/ensure_authenticated.dart | Auth-only redirect middleware |
| lib/app/kernel.dart | Registers middleware aliases |
| lib/app/commands/_index.g.dart | Generated artisan consumer command barrel |
| lib/app/_plugins.g.dart | Generated artisan plugin provider registry |
| ios/RunnerTests/RunnerTests.swift | Adds iOS test target stub |
| ios/Runner/SceneDelegate.swift | iOS scene delegate scaffold |
| ios/Runner/Runner-Bridging-Header.h | iOS bridging header |
| ios/Runner/Info.plist | iOS app plist metadata |
| ios/Runner/Base.lproj/Main.storyboard | iOS main storyboard |
| ios/Runner/Base.lproj/LaunchScreen.storyboard | iOS launch screen storyboard |
| ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md | iOS launch assets readme |
| ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json | iOS launch image assets manifest |
| ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json | iOS app icon asset catalog |
| ios/Runner/AppDelegate.swift | iOS app delegate + implicit engine plugins |
| ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings | iOS workspace settings |
| ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist | iOS workspace checks |
| ios/Runner.xcworkspace/contents.xcworkspacedata | iOS workspace definition |
| ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme | iOS scheme configuration |
| ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings | iOS project workspace settings |
| ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist | iOS project workspace checks |
| ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata | iOS project workspace definition |
| ios/Flutter/Release.xcconfig | iOS Flutter release include |
| ios/Flutter/Debug.xcconfig | iOS Flutter debug include |
| ios/Flutter/AppFrameworkInfo.plist | iOS Flutter framework plist |
| ios/.gitignore | Ignores iOS derived/Pods/ephemeral assets |
| CLAUDE.md | Repo agent/dev workflow + design-first rules |
| bin/fsa | Fast CLI wrapper/build cache for dispatcher |
| bin/dispatcher.dart | Artisan dispatcher wiring for plugins/commands |
| backend/vite.config.js | Vite config with Laravel + Tailwind |
| backend/tests/Unit/ExampleTest.php | Laravel unit test stub |
| backend/tests/TestCase.php | Laravel base test case |
| backend/tests/Feature/ExampleTest.php | Laravel feature test stub |
| backend/storage/logs/.gitignore | Storage ignore rules |
| backend/storage/framework/views/.gitignore | Storage ignore rules |
| backend/storage/framework/testing/.gitignore | Storage ignore rules |
| backend/storage/framework/sessions/.gitignore | Storage ignore rules |
| backend/storage/framework/cache/data/.gitignore | Storage ignore rules |
| backend/storage/framework/cache/.gitignore | Storage ignore rules |
| backend/storage/framework/.gitignore | Storage ignore rules |
| backend/storage/app/public/.gitignore | Storage ignore rules |
| backend/storage/app/private/.gitignore | Storage ignore rules |
| backend/storage/app/.gitignore | Storage ignore rules |
| backend/routes/web.php | Laravel web route (welcome) |
| backend/routes/console.php | Laravel console route stub |
| backend/resources/js/app.js | Laravel frontend JS entry stub |
| backend/resources/css/app.css | Tailwind CSS entry + theme font |
| backend/README.md | Laravel skeleton README |
| backend/public/robots.txt | Robots policy |
| backend/public/index.php | Laravel front controller |
| backend/public/favicon.ico | Backend favicon asset |
| backend/public/.htaccess | Apache rewrite rules |
| backend/phpunit.xml | PHPUnit config for backend |
| backend/package.json | Backend JS deps/scripts |
| backend/lang/vendor/magic-starter/tr/teams.php | TR translation strings for teams |
| backend/lang/vendor/magic-starter/en/teams.php | EN translation strings for teams |
| backend/database/seeders/DatabaseSeeder.php | Seeds a test user |
| backend/database/migrations/2026_06_24_200110_drop_language_column_from_users_table.php | Drops users.language if present |
| backend/database/migrations/2026_06_24_200100_add_two_factor_columns_to_users_table.php | Adds 2FA columns to user model table |
| backend/database/migrations/2026_06_24_200090_add_timezone_to_users_table.php | Adds users.timezone if missing |
| backend/database/migrations/2026_06_24_200080_add_profile_photo_path_to_users_table.php | Adds users.profile_photo_path |
| backend/database/migrations/2026_06_24_200070_add_profile_photo_path_to_teams_table.php | Adds teams.profile_photo_path |
| backend/database/migrations/2026_06_24_200060_add_profile_fields_to_users_table.php | Adds users.phone |
| backend/database/migrations/2026_06_24_200050_add_localization_fields_to_users_table.php | Adds users.locale/timezone |
| backend/database/migrations/2026_06_24_200040_add_guest_and_phone_fields_to_users_table.php | Adds guest/phone fields + alters columns |
| backend/database/migrations/2026_06_24_200030_add_expires_at_to_team_invitations_table.php | Adds team_invitations.expires_at |
| backend/database/migrations/2026_06_24_200020_add_device_info_to_personal_access_tokens_table.php | Adds token ip/user_agent columns |
| backend/database/migrations/2026_06_24_200010_add_current_team_id_to_users_table.php | Adds users.current_team_id FK |
| backend/database/migrations/2026_06_24_100070_create_newsletter_subscribers_table.php | Creates newsletter_subscribers table |
| backend/database/migrations/2026_06_24_100060_create_notification_settings_table.php | Creates notification_settings table |
| backend/database/migrations/2026_06_24_100050_create_notifications_table.php | Creates notifications table |
| backend/database/migrations/2026_06_24_100040_create_personal_access_tokens_table.php | Creates personal_access_tokens table |
| backend/database/migrations/2026_06_24_100030_create_team_invitations_table.php | Creates team_invitations table |
| backend/database/migrations/2026_06_24_100020_create_team_user_table.php | Creates team_user pivot table |
| backend/database/migrations/2026_06_24_100010_create_teams_table.php | Creates teams table |
| backend/database/migrations/0001_01_01_000002_create_jobs_table.php | Creates jobs/batches/failed_jobs |
| backend/database/migrations/0001_01_01_000001_create_cache_table.php | Creates cache/cache_locks tables |
| backend/database/migrations/0001_01_01_000000_create_users_table.php | Creates users/sessions/password_reset tables |
| backend/database/factories/UserFactory.php | User factory definition |
| backend/database/.gitignore | Ignores sqlite db files |
| backend/config/services.php | Third-party service placeholders |
| backend/config/queue.php | Queue connection configuration |
| backend/config/mail.php | Mailers configuration |
| backend/config/logging.php | Backend logging channels config |
| backend/config/filesystems.php | Storage disks and links |
| backend/config/cache.php | Cache stores configuration |
| backend/config/auth.php | Auth providers/guards configuration |
| backend/config/app.php | Backend app core config |
| backend/composer.json | Backend PHP deps (Laravel + magic starter) |
| backend/bootstrap/providers.php | Bootstrap provider list |
| backend/bootstrap/cache/.gitignore | Bootstrap cache ignore rules |
| backend/bootstrap/app.php | Laravel app bootstrap configuration |
| backend/artisan | Laravel artisan entrypoint |
| backend/app/Providers/AppServiceProvider.php | Backend service provider scaffold |
| backend/app/Policies/TeamPolicy.php | Team authorization policy stub |
| backend/app/Models/User.php | User model with Magic Starter traits |
| backend/app/Models/TeamUser.php | TeamUser model passthrough |
| backend/app/Models/TeamInvitation.php | TeamInvitation model passthrough |
| backend/app/Models/Team.php | Team model passthrough + fillable |
| backend/app/Http/Controllers/Controller.php | Backend controller base class |
| backend/app/Actions/MagicStarter/UpdateUserProfile.php | Stub action for profile updates |
| backend/app/Actions/MagicStarter/UpdateUserPassword.php | Stub action for password updates |
| backend/app/Actions/MagicStarter/UpdateTeamMemberRole.php | Stub action for team role updates |
| backend/app/Actions/MagicStarter/UpdateTeam.php | Stub action for team updates |
| backend/app/Actions/MagicStarter/RemoveTeamMember.php | Stub action for removing members |
| backend/app/Actions/MagicStarter/InviteTeamMember.php | Stub action for invitations |
| backend/app/Actions/MagicStarter/DeleteUser.php | Stub action for deleting users |
| backend/app/Actions/MagicStarter/DeleteTeam.php | Stub action for deleting teams |
| backend/app/Actions/MagicStarter/CreateUser.php | Stub action for creating users |
| backend/app/Actions/MagicStarter/CreateTeam.php | Stub action for creating teams |
| backend/app/Actions/MagicStarter/AddTeamMember.php | Stub action for adding members |
| backend/.npmrc | Backend npm safety defaults |
| backend/.gitignore | Backend ignore rules |
| backend/.gitattributes | Backend attributes (diff drivers/export-ignore) |
| backend/.env.example | Backend environment example |
| backend/.editorconfig | Backend editor config |
| android/settings.gradle.kts | Android Gradle settings/plugin management |
| android/gradle/wrapper/gradle-wrapper.properties | Gradle wrapper version pin |
| android/gradle.properties | Android/Gradle flags |
| android/build.gradle.kts | Android root build logic |
| android/app/src/profile/AndroidManifest.xml | Profile manifest permissions |
| android/app/src/main/res/values/styles.xml | Android light launch theme |
| android/app/src/main/res/values-night/styles.xml | Android dark launch theme |
| android/app/src/main/res/drawable/launch_background.xml | Android splash background |
| android/app/src/main/res/drawable-v21/launch_background.xml | Android v21 splash background |
| android/app/src/main/kotlin/com/fluttersdk/magic_example/MainActivity.kt | Android MainActivity |
| android/app/src/main/AndroidManifest.xml | Android app manifest |
| android/app/src/debug/AndroidManifest.xml | Debug manifest permissions |
| android/app/build.gradle.kts | Android app module build config |
| android/.gitignore | Android ignore rules |
| analysis_options.yaml | Dart analyzer/lints configuration |
| AGENTS.md | Agent workflow + design-first rules |
| .metadata | Flutter project metadata |
| .env.example | App env example (API_URL etc.) |
Files not reviewed (3)
- ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Generated file
- ios/Runner.xcworkspace/contents.xcworkspacedata: Generated file
- macos/Runner.xcworkspace/contents.xcworkspacedata: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+13
to
+18
| if (freopen_s(&unused, "CONOUT$", "w", stdout)) { | ||
| _dup2(_fileno(stdout), 1); | ||
| } | ||
| if (freopen_s(&unused, "CONOUT$", "w", stderr)) { | ||
| _dup2(_fileno(stdout), 2); | ||
| } |
Comment on lines
+64
to
+68
| switch (message) { | ||
| case WM_FONTCHANGE: | ||
| flutter_controller_->engine()->ReloadSystemFonts(); | ||
| break; | ||
| } |
Comment on lines
+53
to
+54
| // Theme generated from DESIGN.md via `design:sync`. Regenerate with: | ||
| // dart run magic_example:artisan design:sync |
Comment on lines
+8
to
+10
| 'dialog': { | ||
| 'class': 'bg-white dark:bg-gray-800 rounded-xl p-6 shadow-2xl max-w-lg', | ||
| }, |
Comment on lines
+11
to
+19
| 'confirm': { | ||
| 'container_class': | ||
| 'bg-white dark:bg-gray-800 rounded-xl p-6 shadow-2xl w-80', | ||
| 'title_class': 'text-lg font-bold text-gray-900 dark:text-white', | ||
| 'message_class': 'text-gray-600 dark:text-gray-400 mt-2', | ||
| 'button_cancel_class': 'px-4 py-2 text-gray-600 dark:text-gray-300', | ||
| 'button_confirm_class': 'px-4 py-2 bg-primary text-white rounded-lg', | ||
| 'button_danger_class': 'px-4 py-2 bg-red-500 text-white rounded-lg', | ||
| }, |
Comment on lines
+1
to
+4
| // GENERATED: do not edit by hand. | ||
| // Regenerate via: dart run magic:artisan design:sync | ||
| // | ||
| // Source of truth: DESIGN.md |
Comment on lines
+15
to
+18
| Schema::table('users', function (Blueprint $table) { | ||
| $table->string('email')->nullable()->change(); | ||
| $table->string('password')->nullable()->change(); | ||
|
|
…flow ComponentsPreview is now stateful so the switch, tabs, and select are live (toggle/select to see real behavior). Variant rows use Wind's `wrap` utility instead of the no-op `flex-wrap` so they reflow rather than overflow. The feature-screen scaffold drops its phone-frame MediaQuery override so screens render at their real size in the single-pane catalog.
The app shell picks sidebar-vs-bottom-nav off the window width (wScreenIs reads MediaQuery.size), so the two layouts never show at the same time and the bottom nav was invisible at the catalog's desktop width. Add two dedicated previews that pin each layout by overriding the ambient MediaQuery width: SidebarMenu (desktop rail + user menu, full width) and BottomMenu (mobile header + bottom bar, in a phone frame). ScreenPreviewScaffold gains appDesktop/appMobile chrome modes; feature screens stay bare content. Also flex-1 the dashboard card title so it fits/wraps instead of overflowing (yellow stripe) in the narrow shell.
…ign parity) Match the idea-design catalog's one-component-per-preview model: split the lumped components.preview.dart into dedicated Badge/Button/Card/Input/ Select/Switch/Tabs preview files, each its own auto-discovered section. With the catalog now a single scrolling page, bare screen previews (chrome: none) no longer wrap their own SingleChildScrollView (a nested vertical scroll broke the outer scroll geometry and sidebar scroll-to-section); the catalog page provides the scroll.
SidebarMenu/BottomMenu previews showcase the navigation shell, so their
body is now an empty placeholder card instead of the full DashboardView.
The dashboard's 3-column cards crammed into the narrow content area beside
the sidebar and broke titles mid-word ("Docum entatio n"); a neutral
"Page content" card keeps the focus on the nav chrome.
…talog Register magic_starter's 30 component previews via its dev-only previews.dart barrel (User Profile Dropdown, Team Selector, Notification Dropdown, Accordion, Checkbox, Radio, Dialog, Toast, etc.), merged with the app-owned previews (foundations, screens, nav shells) and sorted by label. The 7 hand-written component preview duplicates are removed; the catalog now mirrors the whole library with no duplication. Registration stays kDebugMode-gated so the starter preview set tree-shakes from release.
Without registerMagicStarterNotificationRoutes() the /settings notification preferences route was never registered, so the hub's Notification Preferences row navigated nowhere.
Introduce lib/ui/components/ as the app-owned component library (folder-per-component: component + recipe + preview + index, discovered by /preview scanning *.preview.dart, no barrel, no re-export aliases). Ship three example components magic_starter does not provide, demonstrating the recipe patterns: Tag (multi-axis WindRecipe intent x size), Callout (single-axis WindRecipe intent), StatCard (static slot classNames). Add pure recipe unit tests.
previews:refresh regenerates lib/_previews.g.dart, but the route provider still imported the stale lib/preview/_previews.g.dart, so newly scaffolded components never reached the catalog. Repoint the import and remove the stale file.
Replace the separate DuskPlugin / TelescopePlugin installs with MagicDevtools.installPre/installPost, add MagicStarter.useWindTheme(windTheme), and fix the stale design:sync command comment.
magic_starter renamed its flat components to MS-prefixed classes; update Card -> MSCard and Typography -> MSTypography (variant enums CardVariant/TypographyVariant unchanged) so the views and foundations preview compile again.
Update the design rule, three skills, the visual-reviewer agent, CLAUDE.md, DESIGN.md, and the component registry from the old magic_starter/lib/src/ui/components target to the app-local lib/ui/components/; add the no-re-export-alias discipline and the hand-authored status-token supplement pattern (generic example).
anilcancakir
added a commit
that referenced
this pull request
Sep 13, 2026
…e command (#13) * Bump the magic ecosystem past the constraint that silently capped it magic_starter alpha.26 needs magic_notifications ^0.2.0, and this app pinned ^0.0.2, which means >=0.0.2 <0.1.0. The two do not intersect, so pub quietly walked the starter back to alpha.24 instead of failing: a fork got a build that resolved, ran, and was two releases stale. The two constraints move together or not at all. alpha.24 also introduced 20 magic_starter.titles.* keys that en.json never gained, and route_service_provider.dart:29 registers the notification routes ungated, so those titles have been rendering as raw keys. The two generated files come along because they are functions of the resolved graph rather than edits: flutter pub get performs a one-time SDK migration on analysis_options.yaml, and the macOS registrant gains purchases_flutter now that magic_payments arrives through the starter. The registrant records the LOCAL graph, which is what keeps the working tree clean. Unlike pubspec.lock, no fork ever consumes this file: any build regenerates it from whatever that fork resolves, so the committed value is transient for them and permanent dirt here would be the only lasting cost. * Wire the two plugins that were registered but never configured config/app.dart registers LocalizationServiceProvider and NotificationServiceProvider, but neither had a config file and neither appeared in main.dart's factory list, so both fell through to package defaults. The notifications feature flag was off besides, which meant a dependency this app pays for shipped undemonstrated. supported_locales already claimed en and tr while assets/lang held only en.json. That claim is now true. The parity between the two files is exact rather than best effort because magic's Translator replaces its sentence map on load instead of merging it with the fallback: a key missing from tr.json renders the raw key path on screen, not the English string. locale_parity_test.dart is what keeps that from drifting back. auto_detect_locale stays off. A device set to a locale this boilerplate does not ship has no tested path here, and the replace-not-merge behaviour above is exactly what turns that into raw keys on every screen. A fork flips it once its supported_locales covers the locales its users actually have. * Close the guardrail gaps a fork would inherit Four independent things, all of which were wrong or missing today. CI gains a published job. Resolving is not the check: pub exits 0 on a downgrade as long as SOME version in range resolves, which is exactly how the alpha.24 walk-back stayed green. The job reads the resolved version out of the lock and asserts it, verified against a synthetic alpha.24 lock to confirm it fails on the input it exists to catch. The component-visual-reviewer agent pointed at /Users/anilcan/Code/fluttersdk/ lib/ui/components, missing this repo's own directory segment. That path does not exist, so every find and grep in its procedure returned nothing and reported a clean review. It was also the only absolute path in a repository whose whole purpose is to be cloned somewhere else. design.md described components as living in the magic_starter generic library on one line and scoped the WindRecipe import rule to magic_starter files on another. Both meant this app's own lib/ui/components/. The import rule itself was right and is unchanged. design-tokens rescanned raw on one trigger, an unterminated block comment at end of file. That catches the OPEN case, which flutter analyze already makes impossible in real Dart. The CLOSED case is the live one: a /* inside a triple-quoted string that some later ordinary */ closes leaves the scanner balanced at EOF, so nothing fires and the region between them is dropped in silence. Measured against the pre-edit script, a raw literal placed that way passed clean. * Track the lock a fork should build from, and gate what gets committed The lock was gitignored because one generated with pubspec_overrides.yaml active encodes sibling paths that exist on one machine. That diagnosis was right and the conclusion was not: the fix for a lock generated the wrong way is to generate it the right way. Untracked, a fork's first build resolves whatever the caret ranges happen to solve to on the day it clones, which is not what CI proved green. run_lockfile is what makes tracking safe. It reads the STAGED lock, not the working tree, and fails on a source: path entry. That split is deliberate and permanent: a local pub get puts sibling paths back in the lock every time, so the working copy stays dirty and unstaged while the index holds the hosted resolution. require_local_siblings reads the opposite state (the overrides file must be PRESENT), and the two cannot wedge because regenerating hosted-only moves that file aside and back rather than removing it. Verified by staging the real local-path lock, 11 path entries, and confirming the job fails with every other job green. An earlier probe that appended a malformed line proved nothing: pub refused to parse it and the run aborted before the gate executed. * Reduce the fork checklist to one command, and demonstrate the patterns app:rename rewrites 24 sites plus the Kotlin package directory: the Dart package name and its import sites, the Android namespace and applicationId, the iOS and macOS bundle identifiers, the three desktop build systems, the web manifest and title, .env, lib/main.dart, DESIGN.md and dependabot.yml. It derives the CURRENT identity from the tree rather than hardcoding it, which is what makes a second run a no-op. --dry-run is proven inert by hashing every file in scope before and after, not by reading git status, which cannot tell a sibling's staged file from a write. Rewrite rules scan for their anchor instead of visiting recorded line numbers. That is why the command handles the sixth PRODUCT_BUNDLE_IDENTIFIER line in the iOS project file, which the research inventory had missed. Two things the repo documented and never demonstrated now have exactly one instance each: a controller implementing SessionScopedController, so the SessionScopeSync.attach() call that has always run finally has something to reset, and a status token family in the supplement shape DESIGN.md describes. The tracked macOS plugin registrant switches to the hosted variant. A fork clones it, runs pub get against the published graph, and gets no diff, so app:rename does not refuse on its first run. The local variant is what made it refuse. The tracked lock already follows this rule for the same reason: what is committed is the graph a fork resolves, not the one this machine does. * Act on the review: four false accessibility claims, two silent data paths The status tokens asserted WCAG AA in their comments and missed it on four of six pairs, measured: white on #16A34A is 3.30, near-black on #B45309 is 3.53, white on #0EA5E9 is 2.77, white on #0284C7 is 4.10. The cause was assuming one foreground per role works in both modes, but the generated fills do not hold lightness across modes and they move in opposite directions: bg-success goes darker to lighter, bg-warning lighter to darker. So each foreground flips by mode now, no generated fill changed, and every pair is recomputed in the docblock: 5.93, 6.40, 5.02, 5.38, 5.57, 5.02. Two paths could act without the operator seeing it. The identity read back out of the tree was never validated, only the flags were, so a pubspec.yaml carrying a traversal in its name reached a directory move; it is now held to the same patterns. And the Kotlin package move skipped every non-File entity and then deleted the source recursively, which would take a fork's subpackages with it, silently, in a case this repo has no fixture for. --display now rejects # and tab. They break nothing syntactically, they get eaten: flutter_dotenv strips a trailing #comment, so --display='Acme #1' left the app reading 'Acme' while the command's own re-read saw the whole string, which quietly breaks idempotency. TEST_HOST on macOS is rewritten after all. The constraint was to touch the Xcode project only through anchored matches, and this rule anchors on the setting name and the current value exactly like the bundle-id rule beside it, so it is an instance of that constraint rather than an exception to it. Left alone it pointed a renamed fork's RunnerTests target at a bundle that is no longer produced. The lockfile gate did not run in CI at all, because CI never invokes bin/check; it now checks the committed lock directly, before pub get rewrites the evidence. The dashboard card was duplicated between its loading and loaded branches, differing only in indentation, and its two strings were hardcoded English on the one screen the rules name as the pattern to copy. * Let magic decide file_picker, instead of pinning it twice Nothing in this app imports file_picker. It arrives through magic's Pick facade, which makes the pin here a second opinion on a version this app has no stake in, and second opinions on a transitive dependency only matter when they disagree with the first one. They did. magic's master moved to ^12.2.0 ahead of a release while this app pinned ^11.0.2 to match published magic 0.0.9. Those ranges do not intersect, so version solving failed outright against local sibling checkouts and bin/check could not run at all, which is the state this repository was already in before any of this work started. Removing the pin resolves file_picker from whichever magic is in play: transitive 12.2.0 against the path-bound local checkout, transitive 11.0.3 against published magic 0.0.9, which is the same version a fork was getting before. Both graphs verified green with the full suite, and the day magic publishes its file_picker 12 port nothing here needs editing. The comment the pin carried argued a fork has no dependency_overrides safety net so the constraint should be repeated here. That was true and still insufficient: magic 0.0.9 already caps file_picker below 12.0.0-0, so the pin never protected anything the framework was not already protecting. * Merge rewrites by path, so two generators cannot cancel each other _buildPlan spread the identity rewrites and the Dart-import scan into one list without merging by path, and the loop re-reads each path from disk. So a file claimed by both generators produced two planned writes from the same original source, and _apply performed them in order: the second discarded the first while the report counted the file as changed twice, with plausible line counts. lib/main.dart is the only path both can claim. It carries the MagicApplication title AND is scanned for a `package:` self-import. This repository never reaches the collision because its own main.dart imports relatively, which is exactly why the fix has to be structural rather than left to a fixture that mirrors this tree: a fork that self-imports loses its display name silently. The test introduces the self-import to reach the case, and fails on the old code with "the display-name rewrite was discarded by the import rewrite". The published job now reads the expected version floor out of pubspec.yaml instead of carrying a literal. A hard-coded alpha.26 would turn the job red on the next routine bump, in a file the bumping author has no reason to open, and name a version nobody typed. The comparison is a floor rather than an equality so a legitimately newer release still passes, verified against alpha.24 (rejected), alpha.26, alpha.27 and 0.0.2. * chore(deps): pin every fluttersdk dependency at the release that exists The nine constraints were written against what was published when this branch opened, and five releases have gone out since: magic 0.0.11, magic_deeplink 0.1.0, magic_notifications 0.3.1, magic_starter 0.0.27 and fluttersdk_artisan 0.0.15. Two of them were not lagging but EXCLUDING. A caret raises the minor whenever the major is zero, so magic_deeplink ^0.0.3 ends at 0.1.0 and magic_notifications ^0.2.0 ends at 0.3.0: the two releases that carry the cold-start push fix this app is the demonstration of could not be resolved here at all. Pub does not report that as a conflict, it walks another package backwards and exits zero, which is the failure this branch exists to close. The macOS plugin registrant moves with them: magic 0.0.10 took file_picker to 12, which splits the plugin into federated platform packages, so the generated import is file_picker_darwin rather than file_picker. It is a tracked generated file and has to match the graph the lock describes. Resolved hosted with no overrides, which is what the published CI job asserts. dart analyze clean, 27 tests green. * fix(rename,config): close four defects this branch introduces app:rename refused a target whose Kotlin package would sit INSIDE the current one only by accident: the guard compared the two paths for equality, and --org=com.fluttersdk.magic_example --name=app names a real identity whose move is the package into a child of itself. The apply step creates the destination first and then lists the source, so the rename landed on the directory it had just made and threw EINVAL after the identity rewrites were already on disk, leaving a half-renamed tree with git as the only undo. It is refused at validation time now, and the message names the two-run route that reaches the same identity without nesting. Equality is excluded, because a display-only rename leaves the Kotlin path untouched. appleId camel-cased only the package, so an org carrying an underscore (com.acme_inc, the ordinary Java encoding of acme-inc.com) produced com.acme_inc.acmeApp in both pbxproj files, and CFBundleIdentifier takes alphanumerics, hyphen and period alone. Every segment is camel-cased now. The report prints androidId and appleId beside the three inputs, because the two ids no longer match character for character and a dry run that hid them made the difference a surprise at code-signing time. lib/config/notifications.dart declared no service worker keys. The web driver drops an absent key from the config it hands the SDK, which then registers OneSignalSDKWorker.js at the root scope that a Flutter web build already owns, so whichever registration lands second wins and push silently never arrives. The two literals match what notifications:install writes into web/, and a fork cannot recover them by re-running that command because it refuses to overwrite an existing config file. database.enabled stays and gains a comment rather than being removed: nothing at runtime gates on it, but notifications:configure prints and rewrites it, and the package's own install stub declares it. Also fixes the floor gate in ci.yml, which compared with sort -V. GNU filevercmp orders an empty suffix below -alpha.1, so a walk-back from 0.0.27 to 0.0.27-alpha.1 sorted the floor first and PASSED the assertion that exists to catch exactly that, and an alpha floor against a stable resolution failed the opposite way. The comparison is semver now, verified against both directions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings up the design-first
magic_examplereference app: the full fluttersdk set (magic, magic_starter, dusk, telescope, magic_deeplink, magic_notifications, magic_social_auth) on fluttersdk_artisan, a Laravelapi/v1backend underbackend/, aDESIGN.md-driven wind theme (design:sync->lib/config/wind_theme.g.dart), a dev-only/previewcomponent catalog (foundations + component library + feature-screen previews via a backend-free mock harness, tree-shaken from release), and the design-first agent infra (CLAUDE.md + AGENTS.md + .claude/rules + skills + component-visual-reviewer + docs/design-culture + component-registry).Verified locally end to end:
flutter analyze0; the app boots and renders login/dashboard/profile + the catalog in light and dark (real-browser e2e); the real backendapi/v1/auth/loginreturns a Sanctum token; release builds strip the catalog.Cross-repo: consumes the sibling
feat/design-first-component-systemPRs (wind #120, magic #102, magic_devtools #7, magic_starter #78) via local path overrides; land those first.