This file defines repository-specific rules for coding agents (including Codex) working on SimpleShoppingList.
Keep this project a clean, scalable SwiftUI scaffold that junior and intermediate developers can adopt and extend.
Prioritize:
- clear architecture boundaries
- safe concurrency
- testable dependencies
- maintainable feature growth
- Keep Swift version compatibility at Swift 6.
- Do not add business logic directly in Views.
- Keep navigation ownership in
Scenetypes. - Keep persistence/state mutation in the store layer (
Services/ShoppingStore.swift), not in views/view models. - Use protocol-driven dependency injection at boundaries.
- Keep store streaming based on
AsyncStream(do not replace with Combine in store layer).
Scenes/: top-level navigation owners (NavigationStack+ destinations)Views/: screen UI onlyComponents/: reusable UI units shared by viewsViewModels/: presentation/business logic, async workflows, derived UI stateServices/: concrete service implementations (including actor-backed store) + service containerProtocols/: dependency contracts (*Protocol)Routes/: route enums used by scene-owned navigationModels/: domain models + sample data
Sceneowns.navigationDestination(for:)for its flow.- Child views/components emit route values, but do not own destination mapping.
- Do not scatter navigation destination wiring across feature views.
- Views render state and forward user actions.
- ViewModels perform async work and hold mutable presentation state.
- If logic grows beyond simple UI glue, move it from View to ViewModel.
- Keep view models
@MainActorwhen publishing UI-facing state. - View starts async work using
Taskand calls async ViewModel methods.
- Store implementation is actor-backed for serialized mutable access.
- Expose change stream using
AsyncStream. - Treat store snapshots as canonical state for view models.
- Avoid manual synchronization primitives when actor isolation already solves the problem.
- Keep file persistence concerns in store layer only.
- Persist shopping data to Documents directory JSON.
- Depend on protocols (
*Protocol) at boundaries. - Compose concrete implementations in app root/container.
- Prefer initializer injection.
- Avoid direct concrete construction deep inside Views/ViewModels.
- Keep dependencies swappable for tests.
Current default in this repo is protocol-driven DI without introducing any broadly.
- Do not introduce
anypervasively unless specifically requested or justified. - If introduced in future, do it intentionally at clear dependency boundaries.
- Preserve the existing visual language unless user asks for a redesign.
- Keep accessibility in mind (readable typography, contrast, VoiceOver labels for key dynamic values).
- Keep reusable styling decisions consistent across views/components.
Follow this sequence:
- Model updates in
Models/ - Protocol updates in
Protocols/ - Store/service updates in
Services/ - ViewModel updates in
ViewModels/ - UI in
Views//Components/ - Route and destination updates in owning
SceneandRoutes/ - Container wiring updates in
Services/or app root
- Prefer tests for store and view model behavior.
- Favor deterministic tests via protocol stubs and injectable dependencies.
- Do not couple tests to real documents directory persistence.
- Keep PRs focused and small when possible.
- Preserve architecture boundaries; do not “quick-fix” by breaking layering.
- Document noteworthy architectural changes in
README.md.