chore(ai): modernize and unify agent tooling and instructions (#5087)

This commit is contained in:
James Rich 2026-04-12 12:29:05 -05:00 committed by GitHub
parent d03e61af6f
commit eeed780e51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 604 additions and 567 deletions

View file

@ -1,52 +0,0 @@
# Agent Playbooks
These playbooks are execution-focused guidance for common changes in this repository.
Use `AGENTS.md` as the source of truth for architecture boundaries and required conventions. If guidance conflicts, follow `AGENTS.md` and current code patterns.
## Version baseline for external docs
When checking upstream docs/examples, match these repository-pinned versions from `gradle/libs.versions.toml`:
- Kotlin: `2.3.20`
- Koin: `4.2.0` (`koin-annotations` `4.2.0` — uses same version as `koin-core`; compiler plugin `0.4.1`)
- JetBrains Navigation 3: `1.1.0-beta01` (`org.jetbrains.androidx.navigation3`)
- JetBrains Lifecycle (multiplatform): `2.11.0-alpha02` (`org.jetbrains.androidx.lifecycle`)
- AndroidX Lifecycle (Android-only): `2.10.0` (`androidx.lifecycle`)
- Kotlin Coroutines: `1.10.2`
- Compose Multiplatform: `1.11.0-beta01`
- JetBrains Material 3 Adaptive: `1.3.0-alpha06` (`org.jetbrains.compose.material3.adaptive`)
Prefer versioned docs pages that match those versions (for example, Koin `4.2` docs rather than older `4.0/4.1` pages).
## Dependency alias quick-reference
Version catalog aliases split cleanly by fork provenance. **Use the right prefix for the right source set.**
| Alias prefix | Coordinates | Use in |
|---|---|---|
| `jetbrains-lifecycle-*` | `org.jetbrains.androidx.lifecycle:*` | `commonMain`, `androidMain` |
| `jetbrains-navigation3-ui` | `org.jetbrains.androidx.navigation3:navigation3-ui` | `commonMain`, `androidMain` |
| `jetbrains-navigationevent-*` | `org.jetbrains.androidx.navigationevent:*` | `commonMain`, `androidMain` |
| `jetbrains-compose-material3-adaptive-*` | `org.jetbrains.compose.material3.adaptive:*` | `commonMain`, `androidMain` |
| `androidx-lifecycle-process` | `androidx.lifecycle:lifecycle-process` | `androidMain` only — `ProcessLifecycleOwner` |
| `androidx-lifecycle-testing` | `androidx.lifecycle:lifecycle-runtime-testing` | `androidUnitTest` only |
> **Note:** JetBrains does not publish a separate `navigation3-runtime` artifact — `navigation3-ui` is the only artifact. The version catalog only defines `jetbrains-navigation3-ui`. The `lifecycle-runtime-ktx` and `lifecycle-viewmodel-ktx` KTX aliases were removed (extensions merged into base artifacts since Lifecycle 2.8.0).
Quick references:
- Koin annotations (4.2 docs): `https://insert-koin.io/docs/reference/koin-annotations/start`
- Koin KMP docs: `https://insert-koin.io/docs/reference/koin-annotations/kmp`
- AndroidX Navigation 3 release notes: `https://developer.android.com/jetpack/androidx/releases/navigation3`
- Kotlin release notes: `https://kotlinlang.org/docs/releases.html`
## Playbooks
- `docs/agent-playbooks/di-navigation3-anti-patterns-playbook.md` - DI and Navigation 3 mistakes to avoid.
- `docs/agent-playbooks/kmp-source-set-bridging-playbook.md` - when to use `expect`/`actual` vs interfaces + app wiring.
- `docs/agent-playbooks/task-playbooks.md` - step-by-step recipes for common implementation tasks, plus code anchor quick reference.
- `docs/agent-playbooks/testing-and-ci-playbook.md` - which Gradle tasks to run based on change type, plus CI parity.

View file

@ -1,58 +0,0 @@
# DI and Navigation 3 Anti-Patterns Playbook
This playbook is a fast guardrail for high-risk mistakes in dependency injection and navigation.
Version note: align guidance with repository-pinned versions in `gradle/libs.versions.toml` (currently Koin `4.2.x` and Navigation 3 JetBrains fork `1.1.x`).
## DI anti-patterns
- Don't put Android framework dependencies (`Context`, `Activity`, `Application`) into shared `commonMain` business logic.
- Do use `@Module`, `@ComponentScan`, and `@KoinViewModel` annotations directly in `commonMain` shared modules. This provides compile-time safety and encapsulates dependency graphs per feature, which is the recommended 2026 KMP practice for Koin 4.x.
- Don't instantiate ViewModels or service dependencies manually in Compose or activities.
- Do resolve app-layer wrappers via Koin (`koinViewModel()` / injected bindings).
- Don't spread DI graph setup across unrelated modules without registration in app startup.
- Do ensure modules are reachable from app bootstrap in `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`.
- Don't assume feature/core `@Module` classes are active automatically.
- Do ensure they are included by the app root module (`@Module(includes = [...])`) in `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt`.
- **Don't use Koin K2 Compiler Plugin's A1 Module Compile Safety checks for inverted dependencies.**
- **Do** leave A1 `compileSafety` disabled in `build-logic/convention/src/main/kotlin/KoinConventionPlugin.kt` (uses typed `KoinGradleExtension`). We rely on Koin's A3 full-graph validation (`startKoin` / `VerifyModule`) to handle our decoupled Clean Architecture design where interfaces are declared in one module and implemented in another.
- **Don't** expect Koin to inject default parameters automatically. The K2 plugin's `skipDefaultValues = true` (default behavior) will cause Koin to skip parameters that have default Kotlin values.
### Current code anchors (DI)
- App-level module scanning: `app/src/main/kotlin/org/meshtastic/app/MainKoinModule.kt`
- App startup + Koin init: `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`
- Shared ViewModel base: `feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt`
- Shared base UI ViewModel: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt`
## Navigation 3 anti-patterns
- Don't reintroduce controller-coupled navigation APIs for shared flow state.
- Do use Navigation 3 types (`NavKey`, `NavBackStack`, `EntryProviderScope`) consistently.
- Don't build route identifiers as ad-hoc strings in feature code when typed route keys already exist.
- Do keep route definitions in `core:navigation` and use typed route objects.
- Don't mutate back navigation with custom stacks disconnected from app backstack.
- Do mutate `NavBackStack<NavKey>` with `add(...)` and `removeLastOrNull()`.
- Don't use Android's `androidx.activity.compose.BackHandler` or custom `PredictiveBackHandler` in multiplatform UI.
- Do use the official KMP `NavigationBackHandler` from `androidx.navigationevent:navigationevent-compose` for back gestures.
- Don't parse deep links manually in platform code or push single routes without a backstack.
- Do use `DeepLinkRouter.route()` in `core:navigation` to synthesize the correct typed backstack from RESTful paths.
- **Don't use a single `NavBackStack` list for multiple tabs, nor reuse the same `NavEntryDecorator` instances across different backstacks.**
- **Do** use `MultiBackstack` (from `core:navigation`) to manage independent `NavBackStack` instances per tab. When rendering the active tab in `MeshtasticNavDisplay`, you **must** supply a fresh set of decorators (using `remember(backStack) { ... }`) bound to the active backstack instance. Failure to swap decorators when swapping backstacks causes Navigation 3 to perceive the inactive entries as "popped", permanently destroying their `ViewModelStore` and saved UI state.
### Current code anchors (Navigation 3)
- Typed routes: `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt`
- Shared saved-state config: `core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/NavigationConfig.kt`
- App root backstack + `MeshtasticNavDisplay`: `app/src/main/kotlin/org/meshtastic/app/ui/Main.kt`
- Shared graph entry provider pattern: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
- Desktop Navigation 3 shell: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
- Desktop nav graph assembly: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
## Quick pre-PR checks for DI/navigation edits
- Verify affected graph/module is registered and reachable from app startup.
- Verify no new Android framework type leaks into `commonMain`.
- Verify routes/backstack use typed keys and Navigation 3 primitives.
- Run targeted verification from `docs/agent-playbooks/testing-and-ci-playbook.md`.

View file

@ -1,45 +0,0 @@
# KMP Source-Set Bridging Playbook
Use this playbook when introducing platform-specific behavior into shared modules.
## 1) Decide if `expect`/`actual` is needed
Use `expect`/`actual` only when a platform API cannot be abstracted cleanly behind an interface passed from app wiring.
- Prefer interface + DI when behavior is already app-owned.
- Prefer `expect`/`actual` for small platform primitives and utilities.
Examples in current code:
- `core/common/src/commonMain/kotlin/org/meshtastic/core/common/util/CommonUri.kt`
- `core/common/src/androidMain/kotlin/org/meshtastic/core/common/util/CommonUri.android.kt`
- `core/repository/src/commonMain/kotlin/org/meshtastic/core/repository/LocationRepository.kt`
## 2) Keep source-set boundaries strict
- `commonMain`: business logic, shared models, coroutine/Flow orchestration.
- `androidMain`: Android framework integration (`Context`, system services, Android SDK).
- `app`: app bootstrap, DI root inclusion, Activity/service wiring, flavor-specific providers.
## 3) Resource and UI bridging rules
- Shared strings/resources must come from `core:resources`.
- Platform/flavor UI implementations should be injected via `CompositionLocal` from app.
Examples:
- Contract (main map): `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt`
- Contract (node tracks): `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/LocalNodeTrackMapProvider.kt`
- Contract (traceroute): `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/LocalTracerouteMapProvider.kt`
- Provider wiring: `app/src/main/kotlin/org/meshtastic/app/MainActivity.kt`
## 4) DI and module activation checks
- If a new feature/core module adds Koin annotations, verify it is included by app root module includes.
- App root includes are defined in `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt`.
## 5) Verification checklist
- No Android-only imports in `commonMain`.
- `expect`/`actual` declarations compile across relevant source sets.
- Routing/DI still resolves from app startup (`MeshUtilApplication`).
- Run verification tasks from `docs/agent-playbooks/testing-and-ci-playbook.md` appropriate to touched modules.

View file

@ -1,113 +0,0 @@
# Task Playbooks
Use these as practical recipes. Keep edits minimal and aligned with existing module boundaries.
For architecture rules and coding standards, see [`AGENTS.md`](../../AGENTS.md).
## Code Anchor Quick Reference
Key files for discovering established patterns:
| Pattern | Reference File |
|---|---|
| App DI wiring | `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt` |
| App startup / Koin bootstrap | `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt` |
| Shared ViewModel | `feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt` |
| `CompositionLocal` platform injection | `app/src/main/kotlin/org/meshtastic/app/MainActivity.kt` |
| Platform abstraction contract | `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt` |
| Node track map provider contract | `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/LocalNodeTrackMapProvider.kt` |
| Traceroute map provider contract | `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/LocalTracerouteMapProvider.kt` |
| Shared strings resource | `core/resources/src/commonMain/composeResources/values/strings.xml` |
| Okio shared I/O | `core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt` |
| `stateInWhileSubscribed` | `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/ViewModelExtensions.kt` |
## Playbook A: Add or update a user-visible string
1. Add/update key in `core/resources/src/commonMain/composeResources/values/strings.xml`.
2. Import generated resource symbol in UI code (`org.meshtastic.core.resources.<key>`).
3. Use `stringResource(Res.string.<key>)` in Compose.
4. If the string appears in a shared dialog, prefer `core:ui` dialog components.
5. Verify no hardcoded user-facing strings were introduced.
Reference examples:
- `feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt`
- `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/AlertDialogs.kt`
## Playbook B: Add shared ViewModel logic in a feature module
1. Implement or extend base ViewModel logic in `feature/<name>/src/commonMain/...`.
2. Keep shared class free of Android framework dependencies.
3. Keep Android framework dependencies out of shared logic; if the module already uses Koin annotations in `commonMain`, keep patterns consistent and ensure app root inclusion.
4. Update navigation entry points in `feature/*/src/androidMain/kotlin/org/meshtastic/feature/*/navigation/...` to resolve ViewModels with `koinViewModel()`.
Reference examples:
- Shared base: `feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt`
- Shared base UI ViewModel: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt`
- Navigation usage: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
- Desktop navigation usage: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
## Playbook C: Add a new dependency or service binding
1. Check `gradle/libs.versions.toml` for existing library and version alias.
2. Add new dependency to version catalog first (if truly new).
3. Wire implementation in the owning module (`core:*`, `feature:*`, or `app`) following existing architecture.
4. Register bindings/modules in app Koin graph where needed.
5. For Android system integration (WorkManager, service bootstrapping), wire via `MeshUtilApplication` and app-layer modules.
Reference examples:
- App startup and Koin bootstrap: `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`
- App module scan: `app/src/main/kotlin/org/meshtastic/app/MainKoinModule.kt`
## Playbook D: Add or modify navigation flow
1. Define/extend route keys in `core:navigation`.
2. Implement feature entry/content using Navigation 3 types (`NavKey`, `NavBackStack`, `EntryProviderScope`).
3. Add graph entries under the relevant feature module's `navigation` package (e.g., `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation`).
4. If the entry content depends on platform-specific UI (e.g. Activity context or specific desktop wrappers), use `expect`/`actual` declarations for the content composables.
5. Use backstack mutation (`add`, `removeLastOrNull`) instead of introducing controller-coupled APIs.
6. Verify deep-link behavior if route is externally reachable.
Reference examples:
- Shared graph wiring: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
- Android specific content: `feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt`
- Desktop specific content: `feature/settings/src/jvmMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt`
- Feature intro graph pattern: `feature/intro/src/androidMain/kotlin/org/meshtastic/feature/intro/IntroNavGraph.kt`
- Desktop nav shell: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
- Desktop nav graph assembly: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
## Playbook E: Add flavor/platform-specific UI implementation
1. Keep shared contracts in `core:ui` or feature shared code.
2. Inject flavor/platform implementation via `CompositionLocal` from `app`.
3. Avoid direct dependency from shared modules to Google Maps/osmdroid/other Android SDK-only APIs.
4. Keep adapter types narrow and stable (interfaces, DTO-like params).
Reference examples:
- Contract (main map): `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt`
- Contract (node tracks): `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/LocalNodeTrackMapProvider.kt`
- Contract (traceroute): `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/LocalTracerouteMapProvider.kt`
- Provider wiring: `app/src/main/kotlin/org/meshtastic/app/MainActivity.kt`
- Consumer side: `feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/MapScreen.kt`
## Playbook F: Onboard a new platform target
1. Create a platform application module (e.g., `desktop/`, `ios/`).
2. Copy `desktop/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt` as the starting stub set. All repository interfaces have no-op implementations there.
3. Create a `<Platform>KoinModule` that mirrors `desktop/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt` — use stubs for unimplemented interfaces, real implementations where available.
4. Add `kotlinx-coroutines-swing` (JVM/Desktop) or the equivalent platform coroutines dispatcher module. Without it, `Dispatchers.Main` is unavailable and any code using `lifecycle.coroutineScope` will crash at runtime.
5. Progressively replace stubs with real implementations (e.g., serial transport for desktop, CoreBluetooth for iOS).
6. Add `<platform>()` target to feature modules as needed (all `core:*` modules already declare `jvm()`).
7. Update CI JVM smoke compile step in `.github/workflows/reusable-check.yml` to include new modules.
8. If `commonMain` code fails to compile for the new target, it's a KMP migration debt — fix the shared code, not the target.
Reference examples:
- Desktop stubs: `desktop/src/main/kotlin/org/meshtastic/desktop/stub/NoopStubs.kt`
- Desktop DI: `desktop/src/main/kotlin/org/meshtastic/desktop/di/DesktopKoinModule.kt`
- Desktop Navigation 3 shell: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
- Desktop nav graph entries: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
- Desktop shared feature wiring: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
- Desktop-specific screen: `feature/settings/src/jvmMain/kotlin/org/meshtastic/feature/settings/DesktopSettingsScreen.kt`
- Roadmap: `docs/roadmap.md`

View file

@ -1,88 +0,0 @@
# Testing and CI Playbook
Use this matrix to choose the right verification depth for a change.
## 1) Baseline local verification order
Run in this order for routine changes:
```bash
./gradlew clean
./gradlew spotlessCheck
./gradlew spotlessApply
./gradlew detekt
./gradlew assembleDebug
./gradlew test
```
Notes:
- This order aligns with repository guidance in `AGENTS.md` and `.github/copilot-instructions.md`.
- CI runs host verification and Android build/device verification in separate jobs inside `.github/workflows/reusable-check.yml`.
## 2) Change-type matrix
- `docs-only` changes:
- Usually no Gradle run required.
- If you touched code examples or command docs, at least run `spotlessCheck` if practical.
- If you changed architecture, CI, validation commands, or agent workflow guidance, update the mirrored docs in `AGENTS.md`, `.github/copilot-instructions.md`, `GEMINI.md`, and `docs/kmp-status.md` in the same slice.
- `UI text/resource` changes:
- `spotlessCheck`, `detekt`, `assembleDebug`.
- `feature/commonMain logic` changes:
- `spotlessCheck`, `detekt`, `test`, `assembleDebug`.
- `navigation/DI wiring` changes (app graph, Koin module/wrapper changes):
- `spotlessCheck`, `detekt`, `assembleDebug`, `test`, plus `testFdroidDebugUnitTest` and `testGoogleDebugUnitTest` when available locally.
- If touching any KMP module, also run `kmpSmokeCompile`.
- `worker/service/background` changes:
- `spotlessCheck`, `detekt`, `assembleDebug`, `test`, and targeted tests around WorkManager/service behavior.
- `BLE/networking/core repository` changes:
- `spotlessCheck`, `detekt`, `assembleDebug`, `test`.
## 3) Flavor and instrumentation checks
Run these when relevant to map/provider/flavor-specific behavior:
```bash
./gradlew lintFdroidDebug lintGoogleDebug
./gradlew testFdroidDebug
./gradlew testGoogleDebug
./gradlew connectedAndroidTest
```
## 4) CI parity checks
Current reusable check workflow includes:
- `spotlessCheck detekt`
- Android lint for all directly runnable Android modules:
`app:lintFdroidDebug app:lintGoogleDebug core:barcode:lintFdroidDebug core:barcode:lintGoogleDebug core:api:lintDebug mesh_service_example:lintDebug`
*(Note: `mesh_service_example:lintDebug` is temporary — the module is deprecated and will be
removed along with its CI tasks in a future release.)*
- Host tests plus coverage aggregation:
`test koverXmlReport app:koverXmlReportFdroidDebug app:koverXmlReportGoogleDebug core:api:koverXmlReportDebug core:barcode:koverXmlReportFdroidDebug core:barcode:koverXmlReportGoogleDebug mesh_service_example:koverXmlReportDebug desktop:koverXmlReport`
*(Note: `mesh_service_example:koverXmlReportDebug` is temporary — see above.)*
- KMP smoke compile lifecycle task (auto-discovers KMP modules and runs JVM + iOS simulator compile checks):
`kmpSmokeCompile`
- Android build tasks:
`app:assembleFdroidDebug app:assembleGoogleDebug mesh_service_example:assembleDebug`
*(Note: `mesh_service_example:assembleDebug` is temporary — see above.)*
- Instrumented tests (when emulator tests are enabled):
`app:connectedFdroidDebugAndroidTest app:connectedGoogleDebugAndroidTest core:barcode:connectedFdroidDebugAndroidTest core:barcode:connectedGoogleDebugAndroidTest`
- Coverage uploads happen once from the host job; instrumented test results upload once from the first Android matrix API to avoid duplicate reporting.
Reference: `.github/workflows/reusable-check.yml`
PR workflow note:
- `.github/workflows/pull-request.yml` ignores docs-only changes (`**/*.md`, `docs/**`), so doc-only PRs may skip Android CI by design.
- PR change detection includes workflow/build/config paths such as `.github/workflows/**`, `desktop/**`, `mesh_service_example/**` (deprecated, will be removed), `config/**`, `gradle/**`, `settings.gradle.kts`, and `test.gradle.kts`.
- Android CI on PRs runs with `run_instrumented_tests: false`; merge queue keeps the full emulator matrix on API 26 and 35.
- Gradle cache writes are enabled for trusted refs/events (`main`, `merge_group`, and `gh-readonly-queue/*`); other refs run in read-only cache mode.
## 5) Practical guidance for agents
- Start with the smallest set that validates your touched area.
- Keep documentation continuously in sync with architecture, CI, and workflow changes; do not defer doc fixes to a later PR.
- If modifying cross-module contracts (routes, repository interfaces, DI graph), run the broader baseline.
- If unable to run full validation locally, report exactly what ran and what remains.

View file

@ -176,5 +176,5 @@ Remaining to be extracted from `:app` or unified in `commonMain`:
- Roadmap: [`docs/roadmap.md`](./roadmap.md)
- Agent guide: [`AGENTS.md`](../AGENTS.md)
- Playbooks: [`docs/agent-playbooks/`](./agent-playbooks/)
- Agent skills: [`.skills/`](../.skills/)
- Decision records: [`docs/decisions/`](./decisions/)