diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..97a920e
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,161 @@
+name: UTO Manager CI
+
+on:
+ push:
+ branches:
+ - main
+ - develop
+ - 'feature/**'
+ pull_request:
+ branches:
+ - main
+ - develop
+
+jobs:
+ # -------------------------------------------------------------------------
+ # Test & Analyze
+ # -------------------------------------------------------------------------
+ test:
+ name: Test & Analyze
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '3.24.0'
+ channel: 'stable'
+ cache: true
+
+ - name: Install dependencies
+ run: flutter pub get
+
+ - name: Run flutter analyze
+ run: flutter analyze --no-fatal-infos
+
+ - name: Run unit tests with coverage
+ run: flutter test --coverage
+
+ - name: Check coverage threshold (80%)
+ run: |
+ pip install lcov-cobertura
+ python -c "
+ import subprocess, sys
+ result = subprocess.run(['lcov', '--summary', 'coverage/lcov.info'],
+ capture_output=True, text=True)
+ output = result.stdout + result.stderr
+ print(output)
+ for line in output.split('\n'):
+ if 'lines' in line.lower() and '%' in line:
+ pct = float(line.split('%')[0].split()[-1])
+ print(f'Coverage: {pct}%')
+ if pct < 80.0:
+ print('ERROR: Coverage below 80% threshold')
+ sys.exit(1)
+ break
+ "
+
+ # -------------------------------------------------------------------------
+ # Build — Windows x64
+ # -------------------------------------------------------------------------
+ build-windows:
+ name: Build Windows x64
+ runs-on: windows-latest
+ needs: test
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '3.24.0'
+ channel: 'stable'
+ cache: true
+
+ - name: Install dependencies
+ run: flutter pub get
+
+ - name: Build Windows release
+ run: flutter build windows --release
+
+ - name: Upload Windows artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: windows-release
+ path: build/windows/x64/runner/Release/
+ retention-days: 30
+
+ # -------------------------------------------------------------------------
+ # Build — Web (WASM)
+ # -------------------------------------------------------------------------
+ build-web:
+ name: Build Web (WASM)
+ runs-on: ubuntu-latest
+ needs: test
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '3.24.0'
+ channel: 'stable'
+ cache: true
+
+ - name: Install dependencies
+ run: flutter pub get
+
+ - name: Build Web (WASM)
+ run: flutter build web --wasm --release
+
+ - name: Upload Web artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: web-release
+ path: build/web/
+ retention-days: 30
+
+ # -------------------------------------------------------------------------
+ # Build — Android APK
+ # -------------------------------------------------------------------------
+ build-android:
+ name: Build Android APK
+ runs-on: ubuntu-latest
+ needs: test
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: Set up Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ flutter-version: '3.24.0'
+ channel: 'stable'
+ cache: true
+
+ - name: Install dependencies
+ run: flutter pub get
+
+ - name: Build Android APK
+ run: flutter build apk --release
+
+ - name: Upload Android artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: android-release
+ path: build/app/outputs/flutter-apk/app-release.apk
+ retention-days: 30
diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml
new file mode 100644
index 0000000..e685d2e
--- /dev/null
+++ b/.idea/libraries/Dart_SDK.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/KotlinJavaRuntime.xml b/.idea/libraries/KotlinJavaRuntime.xml
new file mode 100644
index 0000000..7f4987a
--- /dev/null
+++ b/.idea/libraries/KotlinJavaRuntime.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..a283197
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/.idea/runConfigurations/main_dart.xml b/.idea/runConfigurations/main_dart.xml
new file mode 100644
index 0000000..d75976d
--- /dev/null
+++ b/.idea/runConfigurations/main_dart.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..5ca0cff
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.metadata b/.metadata
new file mode 100644
index 0000000..e1ce952
--- /dev/null
+++ b/.metadata
@@ -0,0 +1,36 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: "ad70ec4617166f1c38e5d2bfd388af71fda14f06"
+ channel: "stable"
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: ad70ec4617166f1c38e5d2bfd388af71fda14f06
+ base_revision: ad70ec4617166f1c38e5d2bfd388af71fda14f06
+ - platform: android
+ create_revision: ad70ec4617166f1c38e5d2bfd388af71fda14f06
+ base_revision: ad70ec4617166f1c38e5d2bfd388af71fda14f06
+ - platform: web
+ create_revision: ad70ec4617166f1c38e5d2bfd388af71fda14f06
+ base_revision: ad70ec4617166f1c38e5d2bfd388af71fda14f06
+ - platform: windows
+ create_revision: ad70ec4617166f1c38e5d2bfd388af71fda14f06
+ base_revision: ad70ec4617166f1c38e5d2bfd388af71fda14f06
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..0883e4b
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,24 @@
+include: package:very_good_analysis/analysis_options.yaml
+
+analyzer:
+ exclude:
+ - "**/*.g.dart"
+ - "**/*.freezed.dart"
+ - lib/infrastructure/database/*.g.dart
+ errors:
+ invalid_annotation_target: ignore
+
+linter:
+ rules:
+ # Allow print statements during development (remove for production)
+ avoid_print: false
+ # Allow public members without docs in non-library code
+ public_member_api_docs: false
+ # Allow single-line if statements
+ curly_braces_in_flow_control_structures: true
+ # Prefer const constructors
+ prefer_const_constructors: true
+ prefer_const_declarations: true
+ # Dart formatter may produce lines > 80 chars for chained method calls;
+ # disable to avoid fighting the formatter.
+ lines_longer_than_80_chars: false
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..c908258
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,14 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+.cxx/
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
new file mode 100644
index 0000000..1860e1f
--- /dev/null
+++ b/android/app/build.gradle.kts
@@ -0,0 +1,45 @@
+plugins {
+ id("com.android.application")
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id("dev.flutter.flutter-gradle-plugin")
+}
+
+android {
+ namespace = "com.utomanager.uto_manager"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "com.utomanager.uto_manager"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+}
+
+kotlin {
+ compilerOptions {
+ jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..8ffe024
--- /dev/null
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4d30e22
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/com/utomanager/uto_manager/MainActivity.kt b/android/app/src/main/kotlin/com/utomanager/uto_manager/MainActivity.kt
new file mode 100644
index 0000000..c714826
--- /dev/null
+++ b/android/app/src/main/kotlin/com/utomanager/uto_manager/MainActivity.kt
@@ -0,0 +1,5 @@
+package com.utomanager.uto_manager
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity : FlutterActivity()
diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..1cb7aa2
--- /dev/null
+++ b/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..8403758
--- /dev/null
+++ b/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..360a160
--- /dev/null
+++ b/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5fac679
--- /dev/null
+++ b/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..8ffe024
--- /dev/null
+++ b/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/android/build.gradle.kts b/android/build.gradle.kts
new file mode 100644
index 0000000..1f88145
--- /dev/null
+++ b/android/build.gradle.kts
@@ -0,0 +1,24 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+val newBuildDir: Directory =
+ rootProject.layout.buildDirectory
+ .dir("../../build")
+ .get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+ val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+ project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean") {
+ delete(rootProject.layout.buildDirectory)
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 0000000..118d7d0
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,6 @@
+org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true
+# This newDsl flag was added by the Flutter template
+android.newDsl=false
+# This builtInKotlin flag was added by the Flutter template
+android.builtInKotlin=false
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
new file mode 100644
index 0000000..6101f5f
--- /dev/null
+++ b/android/settings.gradle.kts
@@ -0,0 +1,26 @@
+pluginManagement {
+ val flutterSdkPath =
+ run {
+ val properties = java.util.Properties()
+ file("local.properties").inputStream().use { properties.load(it) }
+ val flutterSdkPath = properties.getProperty("flutter.sdk")
+ require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+ flutterSdkPath
+ }
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+ id("com.android.application") version "9.0.1" apply false
+ id("org.jetbrains.kotlin.android") version "2.3.20" apply false
+}
+
+include(":app")
diff --git a/android/uto_manager_android.iml b/android/uto_manager_android.iml
new file mode 100644
index 0000000..99e865c
--- /dev/null
+++ b/android/uto_manager_android.iml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/data/mos_dictionary.json b/assets/data/mos_dictionary.json
new file mode 100644
index 0000000..b31be04
--- /dev/null
+++ b/assets/data/mos_dictionary.json
@@ -0,0 +1,615 @@
+{
+ "version": "2026-08-07",
+ "description": "U.S. Army 2026 Doctrine — Authorized MOS Dictionary for UTO Manager",
+ "branches": {
+ "infantry": {
+ "displayName": "Infantry & Maneuver",
+ "cmf": [
+ "11",
+ "15"
+ ],
+ "positions": [
+ {
+ "mos": "11A",
+ "title": "Infantry Officer",
+ "category": "officer",
+ "authorizedRanks": [
+ "O1",
+ "O2",
+ "O3",
+ "O4",
+ "O5",
+ "O6"
+ ]
+ },
+ {
+ "mos": "11B",
+ "title": "Infantryman",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E1",
+ "E2",
+ "E3",
+ "E4",
+ "E5",
+ "E6",
+ "E7",
+ "E8",
+ "E9"
+ ]
+ },
+ {
+ "mos": "11C",
+ "title": "Indirect Fire Infantryman",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E1",
+ "E2",
+ "E3",
+ "E4",
+ "E5",
+ "E6",
+ "E7"
+ ]
+ },
+ {
+ "mos": "15W",
+ "title": "Unmanned Aircraft Systems Operator",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "15A",
+ "title": "Aviation Officer",
+ "category": "officer",
+ "authorizedRanks": [
+ "O1",
+ "O2",
+ "O3",
+ "O4",
+ "O5"
+ ]
+ },
+ {
+ "mos": "15T",
+ "title": "UH-60 Helicopter Repairer",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6",
+ "E7"
+ ]
+ },
+ {
+ "mos": "153A",
+ "title": "Rotary Wing Aviator",
+ "category": "warrant",
+ "authorizedRanks": [
+ "W1",
+ "W2",
+ "W3",
+ "W4",
+ "W5"
+ ]
+ }
+ ],
+ "positionSlots": {
+ "platoonLeader": {
+ "title": "Platoon Leader",
+ "mos": "11A",
+ "authorizedRank": "O2",
+ "isKeyLeader": true,
+ "echelon": "platoon"
+ },
+ "platoonSergeant": {
+ "title": "Platoon Sergeant",
+ "mos": "11B",
+ "authorizedRank": "E7",
+ "isKeyLeader": true,
+ "echelon": "platoon"
+ },
+ "squadLeader": {
+ "title": "Squad Leader",
+ "mos": "11B",
+ "authorizedRank": "E6",
+ "isKeyLeader": true,
+ "echelon": "squad"
+ },
+ "teamLeader": {
+ "title": "Team Leader",
+ "mos": "11B",
+ "authorizedRank": "E5",
+ "isKeyLeader": true,
+ "echelon": "team"
+ },
+ "automaticRifleman": {
+ "title": "Automatic Rifleman (XM250)",
+ "mos": "11B",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "team"
+ },
+ "grenadier": {
+ "title": "Grenadier (XM7+M320)",
+ "mos": "11B",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "team"
+ },
+ "rifleman": {
+ "title": "Rifleman",
+ "mos": "11B",
+ "authorizedRank": "E3",
+ "isKeyLeader": false,
+ "echelon": "team"
+ },
+ "suasOperator": {
+ "title": "sUAS Operator / Rifleman",
+ "mos": "15W",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "team"
+ }
+ }
+ },
+ "medical": {
+ "displayName": "Medical (CMF 68)",
+ "cmf": [
+ "68"
+ ],
+ "positions": [
+ {
+ "mos": "68W",
+ "title": "Combat Medic Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6",
+ "E7"
+ ]
+ },
+ {
+ "mos": "68A",
+ "title": "Biomedical Equipment Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "68J",
+ "title": "Medical Logistics Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E4",
+ "E5",
+ "E6"
+ ]
+ }
+ ],
+ "positionSlots": {
+ "combatMedic": {
+ "title": "Combat Medic Specialist",
+ "mos": "68W",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "platoon"
+ },
+ "medicalEvacNco": {
+ "title": "Medical Evacuation NCO",
+ "mos": "68W",
+ "authorizedRank": "E6",
+ "isKeyLeader": false,
+ "echelon": "company"
+ },
+ "treatmentNco": {
+ "title": "Treatment NCO",
+ "mos": "68W",
+ "authorizedRank": "E6",
+ "isKeyLeader": false,
+ "echelon": "company"
+ }
+ }
+ },
+ "fires": {
+ "displayName": "Field Artillery (CMF 13)",
+ "cmf": [
+ "13"
+ ],
+ "positions": [
+ {
+ "mos": "13A",
+ "title": "Field Artillery Officer",
+ "category": "officer",
+ "authorizedRanks": [
+ "O1",
+ "O2",
+ "O3",
+ "O4",
+ "O5"
+ ]
+ },
+ {
+ "mos": "13B",
+ "title": "Cannon Crewmember",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E1",
+ "E2",
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "13D",
+ "title": "Field Artillery Automated Tactical Data Systems Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "13F",
+ "title": "Fire Support Specialist (JTAC/FSO)",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E4",
+ "E5",
+ "E6",
+ "E7"
+ ]
+ }
+ ],
+ "positionSlots": {
+ "fireSupportSpecialist": {
+ "title": "Joint Fire Support Specialist",
+ "mos": "13F",
+ "authorizedRank": "E5",
+ "isKeyLeader": false,
+ "echelon": "platoon"
+ },
+ "faatdsOperator": {
+ "title": "FAATDS Operator",
+ "mos": "13D",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "platoon"
+ }
+ }
+ },
+ "signal": {
+ "displayName": "Signal (CMF 25)",
+ "cmf": [
+ "25"
+ ],
+ "positions": [
+ {
+ "mos": "25A",
+ "title": "Signal Officer",
+ "category": "officer",
+ "authorizedRanks": [
+ "O1",
+ "O2",
+ "O3",
+ "O4",
+ "O5"
+ ]
+ },
+ {
+ "mos": "25B",
+ "title": "Information Technology Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "25H",
+ "title": "Network Communications Systems Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "25U",
+ "title": "Signal Support Systems Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E4",
+ "E5",
+ "E6"
+ ]
+ }
+ ],
+ "positionSlots": {
+ "networkCommsSpecialist": {
+ "title": "Network Communications Specialist",
+ "mos": "25H",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "platoon"
+ },
+ "itSpecialist": {
+ "title": "Cyber/IT Specialist",
+ "mos": "25B",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "platoon"
+ }
+ }
+ },
+ "cyberEw": {
+ "displayName": "Cyber/EW (CMF 17)",
+ "cmf": [
+ "17"
+ ],
+ "positions": [
+ {
+ "mos": "17A",
+ "title": "Cyber Warfare Officer",
+ "category": "officer",
+ "authorizedRanks": [
+ "O1",
+ "O2",
+ "O3",
+ "O4",
+ "O5"
+ ]
+ },
+ {
+ "mos": "17C",
+ "title": "Cyber Operations Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "17E",
+ "title": "Electronic Warfare Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E4",
+ "E5",
+ "E6"
+ ]
+ }
+ ],
+ "positionSlots": {
+ "ewSpecialist": {
+ "title": "Electronic Warfare Specialist",
+ "mos": "17E",
+ "authorizedRank": "E5",
+ "isKeyLeader": false,
+ "echelon": "platoon"
+ }
+ }
+ },
+ "aviation": {
+ "displayName": "Aviation",
+ "cmf": [
+ "15"
+ ],
+ "positions": [
+ {
+ "mos": "153A",
+ "title": "Rotary Wing Aviator",
+ "category": "warrant",
+ "authorizedRanks": [
+ "W1",
+ "W2",
+ "W3",
+ "W4",
+ "W5"
+ ]
+ },
+ {
+ "mos": "15T",
+ "title": "UH-60 Helicopter Repairer",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "15B",
+ "title": "Aircraft Powerplant Repairer",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "15D",
+ "title": "Aircraft Powertrain Repairer",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "15G",
+ "title": "Aircraft Structural Repairer",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ }
+ ]
+ },
+ "logistics": {
+ "displayName": "Logistics (CMF 91/92)",
+ "cmf": [
+ "91",
+ "92"
+ ],
+ "positions": [
+ {
+ "mos": "91B",
+ "title": "Wheeled Vehicle Mechanic",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "91A",
+ "title": "Wheeled Vehicle Mechanic (Senior)",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E6",
+ "E7",
+ "E8"
+ ]
+ },
+ {
+ "mos": "92A",
+ "title": "Automated Logistical Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "92Y",
+ "title": "Unit Supply Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "92F",
+ "title": "Petroleum Supply Specialist",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ }
+ ],
+ "positionSlots": {
+ "unitSupplySpecialist": {
+ "title": "Unit Supply Specialist",
+ "mos": "92Y",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "company"
+ },
+ "automatedLogisticalSpecialist": {
+ "title": "Automated Logistical Specialist",
+ "mos": "92A",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "company"
+ },
+ "vehicleMechanic": {
+ "title": "Vehicle Mechanic",
+ "mos": "91B",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "company"
+ }
+ }
+ },
+ "engineer": {
+ "displayName": "Engineer (CMF 12)",
+ "cmf": [
+ "12"
+ ],
+ "positions": [
+ {
+ "mos": "12A",
+ "title": "Engineer Officer",
+ "category": "officer",
+ "authorizedRanks": [
+ "O1",
+ "O2",
+ "O3",
+ "O4",
+ "O5"
+ ]
+ },
+ {
+ "mos": "12B",
+ "title": "Combat Engineer",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E1",
+ "E2",
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ },
+ {
+ "mos": "12C",
+ "title": "Bridge Crewmember",
+ "category": "enlisted",
+ "authorizedRanks": [
+ "E3",
+ "E4",
+ "E5",
+ "E6"
+ ]
+ }
+ ],
+ "positionSlots": {
+ "combatEngineer": {
+ "title": "Combat Engineer",
+ "mos": "12B",
+ "authorizedRank": "E4",
+ "isKeyLeader": false,
+ "echelon": "squad"
+ }
+ }
+ }
+ }
+}
diff --git a/development_status.md b/development_status.md
index fbbd5b4..db1cd3b 100644
--- a/development_status.md
+++ b/development_status.md
@@ -1,648 +1,106 @@
-# Development Status
-
-## Flutter-Based Military Unit Task Organization (UTO) Manager
-
-### U.S. Army 2026 Doctrine — Cross-Platform Enterprise System
-
----
-
-> **How to use this document:** Update task statuses as work progresses. Use the status legend below. Record blockers, notes, and completion dates in the Notes column. This document is the single source of truth for sprint tracking and stakeholder reporting.
-
----
-
-## Status Legend
-
-| Symbol | Status | Meaning |
-|---|---|---|
-| ⬜ | `NOT STARTED` | Work has not begun |
-| 🔵 | `IN PROGRESS` | Actively being worked on |
-| ✅ | `COMPLETE` | Done and acceptance criteria verified |
-| ⚠️ | `BLOCKED` | Cannot proceed; blocker documented in Notes |
-| 🔁 | `IN REVIEW` | Implementation complete; pending review/testing |
-| ⏭️ | `DEFERRED` | Intentionally postponed to a later phase |
-
----
-
-## Overall Project Progress
-
-| Phase | Title | Weeks | Status | % Complete |
-|---|---|---|---|---|
-| Phase 1 | Architecture & Data Foundations | 1 – 4 | ⬜ NOT STARTED | 0% |
-| Phase 2 | Canvas Graphics Engine & Gesture Subsystem | 5 – 9 | ⬜ NOT STARTED | 0% |
-| Phase 3 | 2026 Doctrinal Templates & Symbology Engine | 10 – 14 | ⬜ NOT STARTED | 0% |
-| Phase 4 | Persistence, Validation Rules, & Export Engine | 15 – 19 | ⬜ NOT STARTED | 0% |
-| Phase 5 | Platform Optimization, Profiling, & Hardening | 20 – 24 | ⬜ NOT STARTED | 0% |
-
-Overall Project Completion: **0%**
-
-Current Active Phase: —
-
-Project Start Date: ___________
-
-Target Completion Date: ___________
-
----
-
-## Phase 1: Architecture & Data Foundations
-
-### Weeks 1 – 4 | Status: ⬜ NOT STARTED
-
----
-
-### 1.1 — Project Scaffolding & Clean Architecture Setup
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 1.1.1 | Initialize Flutter project with `windows`, `web`, `android` targets | ⬜ | | | | |
-| 1.1.2 | Establish clean architecture folder structure (`domain/`, `application/`, `infrastructure/`, `presentation/`) | ⬜ | | | | |
-| 1.1.3 | Configure `flutter_lints` and `very_good_analysis` lint rules | ⬜ | | | | |
-| 1.1.4 | Set up `pubspec.yaml` with initial dependency declarations | ⬜ | | | | |
-| 1.1.5 | Initialize Git repository with branch protection rules (`main`, `develop`, `feature/*`) | ⬜ | | | | |
-
-**1.1 Acceptance Criteria:**
-
-- [ ] `flutter run -d windows` launches blank scaffold without errors
-- [ ] `flutter run -d chrome` launches blank scaffold without errors
-- [ ] `flutter run -d android` launches blank scaffold without errors
-- [ ] `flutter analyze` passes with zero lint violations
-
----
-
-### 1.2 — Core Domain Entity Definitions
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 1.2.1 | Define `Echelon` enum (Brigade → Team) | ⬜ | | | | |
-| 1.2.2 | Define `DoctrinalBranch` enum (Infantry, Medical, Fires, Signal, CyberEW, Aviation, Logistics, Engineer) | ⬜ | | | | |
-| 1.2.3 | Define `AuthorizedRank` enum (E1–E9, W1–W5, O1–O10) | ⬜ | | | | |
-| 1.2.4 | Define `UnitNode` entity with all required fields | ⬜ | | | | |
-| 1.2.5 | Define `PositionSlot` entity with all required fields | ⬜ | | | | |
-| 1.2.6 | Define `PersonnelRecord` entity with all required fields | ⬜ | | | | |
-| 1.2.7 | Define `DutyMOS` value object with MOS code validation | ⬜ | | | | |
-| 1.2.8 | Build Infantry & Maneuver position slot dictionary (11A, 11B, 15W) | ⬜ | | | | |
-| 1.2.9 | Build Medical position slot dictionary (68W) | ⬜ | | | | |
-| 1.2.10 | Build Field Artillery position slot dictionary (13F, 13D) | ⬜ | | | | |
-| 1.2.11 | Build Signal position slot dictionary (25H, 25B) | ⬜ | | | | |
-| 1.2.12 | Build Cyber/EW position slot dictionary (17E) | ⬜ | | | | |
-| 1.2.13 | Build Logistics position slot dictionary (92Y, 92A, 91B, 91A) | ⬜ | | | | |
-| 1.2.14 | Build Engineer position slot dictionary (12B) | ⬜ | | | | |
-| 1.2.15 | Implement `UnitRepository` abstract interface | ⬜ | | | | |
-| 1.2.16 | Implement `PersonnelRepository` abstract interface | ⬜ | | | | |
-| 1.2.17 | Write unit tests for all domain entity validation logic | ⬜ | | | | |
-| 1.2.18 | Create MOS dictionary JSON data file | ⬜ | | | | |
-
-**1.2 Acceptance Criteria:**
-
-- [ ] 100% unit test coverage on all domain entity validation logic
-- [ ] MOS validation rejects invalid codes
-- [ ] MOS validation accepts all defined specialties across all branches
-
----
-
-### 1.3 — Database Schema & Infrastructure Layer
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 1.3.1 | Add `drift` package; configure for Windows/Android (SQLite) | ⬜ | | | | |
-| 1.3.2 | Configure `drift_web` for Web (IndexedDB) | ⬜ | | | | |
-| 1.3.3 | Define `units` Drift table schema | ⬜ | | | | |
-| 1.3.4 | Define `position_slots` Drift table schema | ⬜ | | | | |
-| 1.3.5 | Define `personnel` Drift table schema | ⬜ | | | | |
-| 1.3.6 | Define `templates` Drift table schema | ⬜ | | | | |
-| 1.3.7 | Implement DAOs for all four tables (CRUD operations) | ⬜ | | | | |
-| 1.3.8 | Configure database migrations framework (versioned migration files) | ⬜ | | | | |
-| 1.3.9 | Implement `JsonTemplateCodec` for unit tree serialization/deserialization | ⬜ | | | | |
-| 1.3.10 | Stub Protobuf schema definitions (`.proto` files) | ⬜ | | | | |
-| 1.3.11 | Write integration tests for all DAO operations | ⬜ | | | | |
-
-**1.3 Acceptance Criteria:**
-
-- [ ] Integration tests pass: CRUD on all four tables
-- [ ] Database migrations run cleanly from version 1 → current
-- [ ] JSON round-trip serialization produces identical domain objects
-
----
-
-### 1.4 — CI/CD Pipeline Configuration
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 1.4.1 | Configure CI pipeline with `flutter build windows` target | ⬜ | | | | |
-| 1.4.2 | Configure CI pipeline with `flutter build web --wasm` target | ⬜ | | | | |
-| 1.4.3 | Configure CI pipeline with `flutter build apk` target | ⬜ | | | | |
-| 1.4.4 | Add `flutter test` and `flutter analyze` automated stages | ⬜ | | | | |
-| 1.4.5 | Configure build artifact upload (`.exe`, `web/`, `.apk`) | ⬜ | | | | |
-| 1.4.6 | Set up code coverage reporting (80% minimum threshold gate) | ⬜ | | | | |
-
-**1.4 Acceptance Criteria:**
-
-- [ ] All three platform builds succeed in CI without manual intervention
-- [ ] Pull requests blocked on failing tests or lint violations
-
----
-
-### Phase 1 Exit Criteria
-
-| Criterion | Status | Verified By | Date |
-|---|---|---|---|
-| Clean architecture folder structure established and enforced by linting | ⬜ | | |
-| All domain entities defined with full unit test coverage | ⬜ | | |
-| Drift database operational on Windows, Web, and Android | ⬜ | | |
-| CI/CD pipeline passing on all three platform targets | ⬜ | | |
-| MOS dictionary data file complete for all functional branches | ⬜ | | |
-
----
-
-## Phase 2: Canvas Graphics Engine & Gesture Subsystem
-
-### Weeks 5 – 9 | Status: ⬜ NOT STARTED
-
-> **Dependency:** Phase 1 must be complete before Phase 2 begins.
-
----
-
-### 2.1 — CustomPainter Canvas Engine Foundation
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 2.1.1 | Implement `UtoCanvasPainter extends CustomPainter` with `paint()` rendering loop | ⬜ | | | | |
-| 2.1.2 | Implement `shouldRepaint()` with dirty-flag comparison | ⬜ | | | | |
-| 2.1.3 | Implement node rendering (`drawRect`, `drawPath`, `drawParagraph`) | ⬜ | | | | |
-| 2.1.4 | Implement parent-child connection line rendering | ⬜ | | | | |
-| 2.1.5 | Wrap painter in `InteractiveViewer` with `TransformationController` | ⬜ | | | | |
-| 2.1.6 | Implement viewport culling via inverse matrix transform (`M⁻¹ × S`) | ⬜ | | | | |
-| 2.1.7 | Define `NodeRenderModel` flat list data structure | ⬜ | | | | |
-| 2.1.8 | Benchmark: verify 60 FPS at 500 placeholder nodes on all three platforms | ⬜ | | | | |
-
-**2.1 Acceptance Criteria:**
-
-- [ ] 60 FPS maintained with 500 placeholder nodes on Windows
-- [ ] 60 FPS maintained with 500 placeholder nodes on Web
-- [ ] 60 FPS maintained with 500 placeholder nodes on Android
-- [ ] Viewport culling demonstrably skips off-screen nodes (verified via paint call counters)
-
----
-
-### 2.2 — Reingold-Tilford Tree Layout Algorithm
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 2.2.1 | Implement Reingold-Tilford algorithm in dedicated `layout/` module | ⬜ | | | | |
-| 2.2.2 | Implement bottom-up subtree width calculation | ⬜ | | | | |
-| 2.2.3 | Implement `(x, y)` coordinate assignment per echelon level | ⬜ | | | | |
-| 2.2.4 | Enforce horizontal branch separation (Maneuver, Fires, Recon, Engineer, Sustainment) | ⬜ | | | | |
-| 2.2.5 | Enforce vertical echelon alignment (Brigade → Individual) | ⬜ | | | | |
-| 2.2.6 | Offload layout computation to background Dart Isolate | ⬜ | | | | |
-| 2.2.7 | Implement `LayoutResult` message passing (Isolate → main thread) | ⬜ | | | | |
-| 2.2.8 | Trigger layout recalculation on: node add, remove, re-parent, template load | ⬜ | | | | |
-
-**2.2 Acceptance Criteria:**
-
-- [ ] Layout correctly positions a full BCT (500+ nodes) without visual overlap
-- [ ] Layout computation completes in < 100ms for a full BCT on Windows
-- [ ] UI thread remains unblocked during layout recalculation (verified via DevTools)
-
----
-
-### 2.3 — Quadtree Spatial Index
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 2.3.1 | Implement `Quadtree` in pure Dart | ⬜ | | | | |
-| 2.3.2 | Implement node bounding box insert on layout completion | ⬜ | | | | |
-| 2.3.3 | Implement nearest-neighbor proximity query | ⬜ | | | | |
-| 2.3.4 | Implement index update on node position change (drag) | ⬜ | | | | |
-| 2.3.5 | Implement entry removal on node deletion | ⬜ | | | | |
-| 2.3.6 | Integrate Quadtree with canvas hit-testing pipeline | ⬜ | | | | |
-| 2.3.7 | Implement screen-space → canvas-space coordinate transform via `M⁻¹` | ⬜ | | | | |
-| 2.3.8 | Write unit tests covering edge cases (overlapping bounds, boundary nodes) | ⬜ | | | | |
-
-**2.3 Acceptance Criteria:**
-
-- [ ] Hit-testing correctly identifies nodes in a 1,000-node tree with < 1ms query time
-- [ ] No false positives or missed hits in unit tests
-
----
-
-### 2.4 — Cross-Platform Drag-and-Drop State Machine
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 2.4.1 | Implement `Idle` state: canvas responds to pan/zoom only | ⬜ | | | | |
-| 2.4.2 | Implement `DragInitiated` state: node selected, subtree isolated to overlay | ⬜ | | | | |
-| 2.4.3 | Implement `DragInProgress` state: Quadtree proximity queries + validation highlights | ⬜ | | | | |
-| 2.4.4 | Implement `DropExecuted` state: re-parenting command, DB update, layout recalc | ⬜ | | | | |
-| 2.4.5 | Implement desktop gesture threshold: drag after 2-pixel mouse movement | ⬜ | | | | |
-| 2.4.6 | Implement mobile gesture threshold: 200ms long-press + haptic feedback | ⬜ | | | | |
-| 2.4.7 | Integrate `super_drag_and_drop` package for cross-component payload integrity | ⬜ | | | | |
-| 2.4.8 | Implement `DragOverlayPainter` as isolated `RepaintBoundary` layer | ⬜ | | | | |
-| 2.4.9 | Implement `AttachNodeCommand` as undoable command object | ⬜ | | | | |
-| 2.4.10 | Implement `DetachNodeCommand` as undoable command object | ⬜ | | | | |
-| 2.4.11 | Implement undo/redo stack for node re-parenting operations | ⬜ | | | | |
-| 2.4.12 | Implement green highlight for valid drop targets | ⬜ | | | | |
-| 2.4.13 | Implement red indicator for invalid drop targets | ⬜ | | | | |
-
-**2.4 Acceptance Criteria:**
-
-- [ ] Dragging Rifle Squad to Infantry Platoon shows green highlight and completes re-parenting
-- [ ] Dragging Fire Team to Brigade HQ shows red invalid indicator
-- [ ] 200ms long-press threshold prevents accidental drags on touch devices
-- [ ] Undo (Ctrl+Z / gesture) correctly reverses re-parenting operations
-- [ ] Drag overlay does not trigger full canvas repaint (verified via DevTools)
-
----
-
-### Phase 2 Exit Criteria
-
-| Criterion | Status | Verified By | Date |
-|---|---|---|---|
-| CustomPainter canvas engine rendering nodes and connection lines with viewport culling | ⬜ | | |
-| Reingold-Tilford layout algorithm operational in background Dart Isolate | ⬜ | | |
-| Quadtree spatial index integrated with hit-testing pipeline | ⬜ | | |
-| Cross-platform drag-and-drop state machine functional on Windows and Android | ⬜ | | |
-| 60 FPS maintained at target node counts on all three platforms | ⬜ | | |
-
----
-
-## Phase 3: 2026 Doctrinal Templates & Symbology Engine
-
-### Weeks 10 – 14 | Status: ⬜ NOT STARTED
-
-> **Dependency:** Phase 2 must be complete before Phase 3 begins.
-
----
-
-### 3.1 — Doctrinal Template Library
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 3.1.1 | Implement JSON-driven runtime template loader | ⬜ | | | | |
-| 3.1.2 | Build MBCT JSON template (full brigade structure) | ⬜ | | | | |
-| 3.1.3 | Build SBCT JSON template (full brigade structure) | ⬜ | | | | |
-| 3.1.4 | Build ABCT JSON template (full brigade structure) | ⬜ | | | | |
-| 3.1.5 | Build CAB-Heavy JSON template | ⬜ | | | | |
-| 3.1.6 | Build CAB-Light JSON template | ⬜ | | | | |
-| 3.1.7 | Implement `TemplateInstantiator` service (JSON → `UnitNode` tree → Drift DB) | ⬜ | | | | |
-| 3.1.8 | Implement runtime template reload mechanism (no binary recompilation required) | ⬜ | | | | |
-| 3.1.9 | Write integration tests for `TemplateInstantiator` | ⬜ | | | | |
-
-**3.1 Acceptance Criteria:**
-
-- [ ] MBCT template instantiation creates correct unit/slot/personnel counts in DB
-- [ ] SBCT template instantiation creates correct unit/slot/personnel counts in DB
-- [ ] ABCT template instantiation creates correct unit/slot/personnel counts in DB
-- [ ] CAB-Heavy template instantiation creates correct unit/slot/personnel counts in DB
-- [ ] CAB-Light template instantiation creates correct unit/slot/personnel counts in DB
-- [ ] Template instantiation completes in < 2 seconds for a full BCT
-- [ ] Updated JSON template files reload correctly without app restart
-
----
-
-### 3.2 — MIL-STD-2525D Symbology Engine
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 3.2.1 | Integrate `milsymbol.js` via `dart:js_interop` for Web target | ⬜ | | | | |
-| 3.2.2 | Implement SIDC → milsymbol.js → SVG path pipeline for Web | ⬜ | | | | |
-| 3.2.3 | Bundle SVG assets: affiliation frames (Friendly, Hostile, Neutral, Unknown) | ⬜ | | | | |
-| 3.2.4 | Bundle SVG assets: function icons (infantry, medical, signal, artillery, engineer, aviation, EW, UAV, logistics) | ⬜ | | | | |
-| 3.2.5 | Bundle SVG assets: echelon modifiers (Team through Division) | ⬜ | | | | |
-| 3.2.6 | Implement SVG path parsing via `path_drawing` into immutable `Path` objects at app launch | ⬜ | | | | |
-| 3.2.7 | Implement thread-safe LRU cache for parsed `Path` objects (indexed by SIDC) | ⬜ | | | | |
-| 3.2.8 | Implement `SidcResolver` mapping `(DoctrinalBranch, Echelon, Affiliation)` → 15-digit SIDC | ⬜ | | | | |
-| 3.2.9 | Implement `TacticalSymbolPainter`: Frame + Icon + Echelon Modifier + Text Designator | ⬜ | | | | |
-| 3.2.10 | Integrate `TacticalSymbolPainter` into main `UtoCanvasPainter` node rendering loop | ⬜ | | | | |
-
-**3.2 Acceptance Criteria:**
-
-- [ ] All defined branch/echelon combinations render correct MIL-STD-2525D symbols
-- [ ] Symbol cache hit rate > 95% during normal canvas rendering
-- [ ] Symbol rendering adds < 2ms overhead per frame on Android
-- [ ] Web target renders symbols via milsymbol.js without visible quality difference from native
-
----
-
-### 3.3 — Three-Pane UI Shell
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 3.3.1 | Implement application header (Echelon Selector, Doctrinal Mode, Export Tools) | ⬜ | | | | |
-| 3.3.2 | Implement left panel: collapsible template palette sidebar | ⬜ | | | | |
-| 3.3.3 | Implement left panel: template drag sources (MBCT, SBCT, ABCT, CAB, echelon blocks, duty slots) | ⬜ | | | | |
-| 3.3.4 | Implement central canvas workspace integration (canvas + InteractiveViewer) | ⬜ | | | | |
-| 3.3.5 | Implement right panel: context-sensitive node inspector | ⬜ | | | | |
-| 3.3.6 | Implement right panel: unit ID, MOS, personnel search, weapon assignment, readiness status | ⬜ | | | | |
-| 3.3.7 | Implement status bar (scale factor, active node count, MTOE validation status) | ⬜ | | | | |
-| 3.3.8 | Implement keyboard shortcuts: `Ctrl+Z`, `Ctrl+Y`, `Ctrl+F`, `Ctrl+D`, `Ctrl+S` | ⬜ | | | | |
-| 3.3.9 | Implement mouse-wheel pan/zoom | ⬜ | | | | |
-| 3.3.10 | Implement multi-node box selection | ⬜ | | | | |
-| 3.3.11 | Implement right-click context menus (desktop) | ⬜ | | | | |
-| 3.3.12 | Implement Android navigation drawer (replaces left panel) | ⬜ | | | | |
-| 3.3.13 | Implement Android bottom sheet inspector (replaces right panel) | ⬜ | | | | |
-| 3.3.14 | Implement tap-and-hold radial menus (Android, replaces right-click) | ⬜ | | | | |
-| 3.3.15 | Verify all touch targets ≥ 48×48 dp on Android | ⬜ | | | | |
-
-**3.3 Acceptance Criteria:**
-
-- [ ] Three-pane layout renders correctly at 1920×1080 on Windows
-- [ ] Three-pane layout renders correctly at 2560×1440 on Windows
-- [ ] Mobile layout correctly switches to drawer/bottom-sheet on Android
-- [ ] All keyboard shortcuts function correctly on Windows and Web
-- [ ] Touch targets meet 48×48 dp minimum on Android
-
----
-
-### Phase 3 Exit Criteria
-
-| Criterion | Status | Verified By | Date |
-|---|---|---|---|
-| All five BCT doctrinal templates instantiable from JSON | ⬜ | | |
-| MIL-STD-2525D symbology rendering on all three platforms | ⬜ | | |
-| Three-pane UI shell functional on desktop and adaptive on Android | ⬜ | | |
-| Runtime template reload mechanism operational | ⬜ | | |
-
----
-
-## Phase 4: Persistence, Validation Rules, & Export Engine
-
-### Weeks 15 – 19 | Status: ⬜ NOT STARTED
-
-> **Dependency:** Phase 3 must be complete before Phase 4 begins.
-
----
-
-### 4.1 — Full Persistence Integration & Offline-First Architecture
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 4.1.1 | Complete Drift repository implementations for all four tables | ⬜ | | | | |
-| 4.1.2 | Implement reactive Drift `watchAll()` streams feeding BLoC state | ⬜ | | | | |
-| 4.1.3 | Implement `CommandEventStore` with local offline queue | ⬜ | | | | |
-| 4.1.4 | Implement `AttachNodeEvent` command event type | ⬜ | | | | |
-| 4.1.5 | Implement `DetachNodeEvent` command event type | ⬜ | | | | |
-| 4.1.6 | Implement `AssignPersonnelEvent` command event type | ⬜ | | | | |
-| 4.1.7 | Implement `UnassignPersonnelEvent` command event type | ⬜ | | | | |
-| 4.1.8 | Implement `ModifySlotEvent` command event type | ⬜ | | | | |
-| 4.1.9 | Implement `DeltaSynchronizer` (transmit queued events on reconnection) | ⬜ | | | | |
-| 4.1.10 | Implement conflict resolution strategy (last-write-wins + conflict flagging) | ⬜ | | | | |
-| 4.1.11 | Configure `drift_web` for IndexedDB persistence on Web target | ⬜ | | | | |
-| 4.1.12 | Implement database backup/restore (export to encrypted JSON archive) | ⬜ | | | | |
-
-**4.1 Acceptance Criteria:**
-
-- [ ] Application continues full operation with zero network connectivity
-- [ ] Delta synchronizer transmits only changed events (not full model) on reconnection
-- [ ] Reactive streams update UI within 16ms of database write
-- [ ] Backup/restore round-trip produces identical database state
-
----
-
-### 4.2 — Real-Time MTOE Domain Validation Engine
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 4.2.1 | Implement `MtoeValidationEngine` in background Dart Isolate | ⬜ | | | | |
-| 4.2.2 | Implement MOS Mismatch Detection rule | ⬜ | | | | |
-| 4.2.3 | Implement Rank Mismatch Detection rule | ⬜ | | | | |
-| 4.2.4 | Implement Vacant Leadership Slot Detection rule | ⬜ | | | | |
-| 4.2.5 | Implement Missing Critical Support Role Detection rule (68W, 13F) | ⬜ | | | | |
-| 4.2.6 | Implement Structural Hierarchy Violation Detection rule | ⬜ | | | | |
-| 4.2.7 | Implement Doctrinal Strength Deviation rule (< 70% manning) | ⬜ | | | | |
-| 4.2.8 | Implement `ValidationFinding` result model (severity, unitId, slotId, findingCode, description) | ⬜ | | | | |
-| 4.2.9 | Integrate validation findings into status bar (aggregate counts by severity) | ⬜ | | | | |
-| 4.2.10 | Integrate validation findings into node visual indicators (colored border/badge) | ⬜ | | | | |
-| 4.2.11 | Integrate validation findings into right panel inspector (detailed finding list) | ⬜ | | | | |
-| 4.2.12 | Implement drag-and-drop pre-validation (evaluate before drop execution) | ⬜ | | | | |
-
-**4.2 Acceptance Criteria:**
-
-- [ ] All six validation rules correctly identify violations in test unit trees
-- [ ] Validation engine processes a full BCT (500+ nodes) in < 500ms in background Isolate
-- [ ] Node visual indicators update within one frame of validation completion
-- [ ] Dragging Fire Team to Brigade HQ shows red pre-validation indicator before drop
-
----
-
-### 4.3 — Data Export Engine
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 4.3.1 | Implement JSON export module (full unit tree serialization) | ⬜ | | | | |
-| 4.3.2 | Implement Protobuf export module (binary-encoded unit tree) | ⬜ | | | | |
-| 4.3.3 | Implement vector PDF export: org chart rendering via `pdf` package | ⬜ | | | | |
-| 4.3.4 | Implement PDF export: MIL-STD-2525D symbols as vector paths | ⬜ | | | | |
-| 4.3.5 | Implement PDF export: configurable page sizes (Letter, A4, Tabloid/A3) | ⬜ | | | | |
-| 4.3.6 | Implement PDF export: cover page with unit designation, date, classification placeholder | ⬜ | | | | |
-| 4.3.7 | Implement JSON import pipeline with doctrinal validation | ⬜ | | | | |
-| 4.3.8 | Implement export UI dialog (format selection, scope selection, file save) | ⬜ | | | | |
-| 4.3.9 | Offload JSON/Protobuf serialization to background Dart Isolate | ⬜ | | | | |
-| 4.3.10 | Offload PDF rendering to background Dart Isolate | ⬜ | | | | |
-
-**4.3 Acceptance Criteria:**
-
-- [ ] JSON export/import round-trip produces identical unit tree
-- [ ] Protobuf export file size < 50% of equivalent JSON for a full BCT
-- [ ] PDF export renders all MIL-STD-2525D symbols as vector paths (not rasterized)
-- [ ] PDF export completes in < 10 seconds for a full BCT on Windows
-
----
-
-### Phase 4 Exit Criteria
-
-| Criterion | Status | Verified By | Date |
-|---|---|---|---|
-| Full reactive Drift persistence layer operational on all three platforms | ⬜ | | |
-| Offline-first operation verified with delta synchronization | ⬜ | | |
-| MTOE validation engine detecting all defined violation types in real time | ⬜ | | |
-| JSON, Protobuf, and vector PDF export modules functional | ⬜ | | |
-| JSON import pipeline with doctrinal validation | ⬜ | | |
-
----
-
-## Phase 5: Platform Optimization, Profiling, & Hardening
-
-### Weeks 20 – 24 | Status: ⬜ NOT STARTED
-
-> **Dependency:** Phase 4 must be complete before Phase 5 begins.
-
----
-
-### 5.1 — Performance Profiling & Rendering Optimization
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 5.1.1 | Profile Windows target: frame timeline, memory, CPU via Flutter DevTools | ⬜ | | | | |
-| 5.1.2 | Profile Web (WASM) target: frame timeline, memory, CPU via Flutter DevTools | ⬜ | | | | |
-| 5.1.3 | Profile Android target: frame timeline, memory, CPU via Flutter DevTools | ⬜ | | | | |
-| 5.1.4 | Apply `RepaintBoundary` isolation: static layer (grid, connection lines) | ⬜ | | | | |
-| 5.1.5 | Apply `RepaintBoundary` isolation: dynamic layer (drag preview overlay) | ⬜ | | | | |
-| 5.1.6 | Apply `RepaintBoundary` isolation: node layer (individual unit nodes) | ⬜ | | | | |
-| 5.1.7 | Optimize Reingold-Tilford: cache subtree widths, skip unchanged subtrees | ⬜ | | | | |
-| 5.1.8 | Optimize Quadtree: bulk-insert for template instantiation, lazy rebuild | ⬜ | | | | |
-| 5.1.9 | Tune LRU symbol cache capacity per platform (Windows: 2,000 / Android: 500 / Web: 1,000) | ⬜ | | | | |
-| 5.1.10 | Implement progressive loading for large templates (visible viewport first) | ⬜ | | | | |
-| 5.1.11 | Build automated performance benchmark suite (frame rate tests at target node counts) | ⬜ | | | | |
-
-**5.1 Acceptance Criteria:**
-
-- [ ] Windows: 60 FPS at 5,000+ nodes (verified by automated benchmark)
-- [ ] Web (WASM): 60 FPS at 2,000+ nodes (verified by automated benchmark)
-- [ ] Android: 60 FPS at 1,500+ nodes (verified by automated benchmark)
-- [ ] No memory leaks over 30-minute continuous operation sessions
-- [ ] Main thread frame time < 8ms during layout recalculation
-
----
-
-### 5.2 — Heavy Computation Isolate Offloading
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 5.2.1 | Audit and confirm: Reingold-Tilford layout → background Isolate | ⬜ | | | | |
-| 5.2.2 | Audit and confirm: MTOE validation engine → background Isolate | ⬜ | | | | |
-| 5.2.3 | Offload JSON/Protobuf serialization → background Isolate | ⬜ | | | | |
-| 5.2.4 | Offload PDF rendering → background Isolate | ⬜ | | | | |
-| 5.2.5 | Implement `IsolatePool` for managing concurrent background computations | ⬜ | | | | |
-| 5.2.6 | Implement progress reporting from Isolates to UI | ⬜ | | | | |
-| 5.2.7 | Implement Isolate cancellation for superseded layout requests | ⬜ | | | | |
-
-**5.2 Acceptance Criteria:**
-
-- [ ] Main UI thread frame time remains < 8ms during any background computation
-- [ ] Isolate cancellation correctly aborts superseded layout requests
-- [ ] Progress indicators display for all operations > 500ms
-
----
-
-### 5.3 — Platform-Specific Hardening
-
-Windows x64
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 5.3.1 | Implement multi-window support (multiple UTO workspaces simultaneously) | ⬜ | | | | |
-| 5.3.2 | Verify high-DPI scaling at 100%, 125%, 150%, 200% display scaling | ⬜ | | | | |
-| 5.3.3 | Implement system tray integration and window state persistence | ⬜ | | | | |
-| 5.3.4 | Configure `.msi` installer with registry entries and uninstall support | ⬜ | | | | |
-| 5.3.5 | Implement Windows file association for `.uto` project files | ⬜ | | | | |
-
-Web (WASM/CanvasKit)
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 5.3.6 | Optimize WASM bundle size (tree-shaking, deferred template asset loading) | ⬜ | | | | |
-| 5.3.7 | Implement IndexedDB storage quota management and user warnings | ⬜ | | | | |
-| 5.3.8 | Implement memory-bounded canvas surface allocation | ⬜ | | | | |
-| 5.3.9 | Configure service worker for offline PWA capability | ⬜ | | | | |
-| 5.3.10 | Test across Chrome, Edge, and Firefox | ⬜ | | | | |
-
-Android
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 5.3.11 | Implement battery profiling: reduce Isolate activity when battery < 20% | ⬜ | | | | |
-| 5.3.12 | Verify all touch targets ≥ 48×48 dp across all interactive elements | ⬜ | | | | |
-| 5.3.13 | Implement Android back-gesture handling for panel navigation | ⬜ | | | | |
-| 5.3.14 | Configure `.aab` release build with ProGuard/R8 optimization | ⬜ | | | | |
-| 5.3.15 | Test on Android 12, 13, and 14 (tablet and phone form factors) | ⬜ | | | | |
-
-**5.3 Acceptance Criteria:**
-
-- [ ] Windows installer installs/uninstalls cleanly; `.uto` file association works
-- [ ] Web app functions offline after initial load (PWA service worker active)
-- [ ] Android app passes battery profiling: no excessive background drain
-- [ ] All touch targets verified ≥ 48×48 dp on Android
-
----
-
-### 5.4 — Security, Accessibility, & Final QA
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 5.4.1 | Implement classification marking placeholder system (UNCLASSIFIED / FOUO banners) | ⬜ | | | | |
-| 5.4.2 | Integrate SQLCipher for data-at-rest database encryption | ⬜ | | | | |
-| 5.4.3 | Implement platform keychain key storage (Windows Credential Manager / Android Keystore) | ⬜ | | | | |
-| 5.4.4 | Conduct screen reader compatibility audit (Windows Narrator, Android TalkBack) | ⬜ | | | | |
-| 5.4.5 | Verify WCAG 2.1 AA contrast ratios for all text elements | ⬜ | | | | |
-| 5.4.6 | Verify keyboard-only navigation completeness on Windows/Web | ⬜ | | | | |
-| 5.4.7 | Execute E2E test: template instantiation → drag-and-drop → personnel assignment → export | ⬜ | | | | |
-| 5.4.8 | Execute E2E test: offline operation → reconnection → delta synchronization | ⬜ | | | | |
-| 5.4.9 | Execute E2E test: validation finding detection → resolution → re-validation | ⬜ | | | | |
-| 5.4.10 | Conduct load test: 10 simultaneous full BCT templates, 10-minute stability | ⬜ | | | | |
-
-**5.4 Acceptance Criteria:**
-
-- [ ] Database encryption verified: raw SQLite file unreadable without key
-- [ ] All WCAG 2.1 AA contrast requirements met
-- [ ] All E2E tests pass with 100% success rate on all three platforms
-- [ ] Load test: 10 simultaneous BCT templates stable for 10 minutes without crash or memory leak
-
----
-
-### 5.5 — Release Package Assembly
-
-| # | Task | Status | Assignee | Start Date | End Date | Notes |
-|---|---|---|---|---|---|---|
-| 5.5.1 | Assemble Windows release: signed `.msi` installer + portable `.exe` archive | ⬜ | | | | |
-| 5.5.2 | Assemble Web release: optimized WASM bundle + static hosting configuration | ⬜ | | | | |
-| 5.5.3 | Assemble Android release: signed `.aab` (Google Play) + sideload `.apk` | ⬜ | | | | |
-| 5.5.4 | Generate release notes (features, known limitations, upgrade instructions) | ⬜ | | | | |
-| 5.5.5 | Tag release in version control (`v1.0.0`) | ⬜ | | | | |
-| 5.5.6 | Archive build artifacts with SHA-256 checksums | ⬜ | | | | |
-
-**5.5 Acceptance Criteria:**
-
-- [ ] All three release packages install and launch correctly on clean target systems
-- [ ] SHA-256 checksums verified for all artifacts
-- [ ] Release tagged in version control
-
----
-
-### Phase 5 Exit Criteria
-
-| Criterion | Status | Verified By | Date |
-|---|---|---|---|
-| All three platforms meet FPS targets at specified node counts | ⬜ | | |
-| All heavy computations confirmed offloaded to background Isolates | ⬜ | | |
-| Platform-specific hardening complete (Windows multi-window, Web PWA, Android battery) | ⬜ | | |
-| Database encryption integrated | ⬜ | | |
-| End-to-end test suite passing on all three platforms | ⬜ | | |
-| Release packages assembled and signed for all three platforms | ⬜ | | |
-
----
-
-## Blockers & Issues Log
-
-| # | Date Raised | Phase | Task # | Description | Severity | Status | Resolution | Date Resolved |
-|---|---|---|---|---|---|---|---|---|
-| — | — | — | — | No blockers recorded | — | — | — | — |
-
----
-
-## Milestone Summary
-
-| Milestone | Target Week | Deliverable | Status |
-|---|---|---|---|
-| M1 | End of Week 4 | Compilable multi-platform skeleton + Drift DB + CI pipeline | ⬜ |
-| M2 | End of Week 9 | Functional canvas engine with drag-and-drop on Windows and Android | ⬜ |
-| M3 | End of Week 14 | All BCT templates instantiable + MIL-STD-2525D symbols rendering | ⬜ |
-| M4 | End of Week 19 | Full persistence + real-time MTOE validation + JSON/PDF export | ⬜ |
-| M5 | End of Week 24 | Signed release packages for Windows, Web, and Android | ⬜ |
-
----
-
-## Task Count Summary
-
-| Phase | Total Tasks | Complete | In Progress | Blocked | Not Started |
-|---|---|---|---|---|---|
-| Phase 1 | 30 | 0 | 0 | 0 | 30 |
-| Phase 2 | 29 | 0 | 0 | 0 | 29 |
-| Phase 3 | 34 | 0 | 0 | 0 | 34 |
-| Phase 4 | 30 | 0 | 0 | 0 | 30 |
-| Phase 5 | 33 | 0 | 0 | 0 | 33 |
-| **Total** | **156** | **0** | **0** | **0** | **156** |
-
----
-
-Document Version: 1.0 | Generated: 2026-08-07 | Companion to: phased_development_plan.md
-
-Update this document at the start and end of each work session.
+# UTO Manager — Development Status
+
+**Last Updated:** 2026-08-07
+**Current Branch:** `feature/phase-1-foundations` → merged to `main`
+**Active Phase:** Phase 1 — Architecture & Data Foundations ✅ COMPLETE
+
+---
+
+## Phase 1 — Architecture & Data Foundations ✅ COMPLETE
+
+### Completed Items
+
+| Item | Status | Notes |
+|------|--------|-------|
+| Flutter project initialization | ✅ | Multi-platform: Windows, Web (WASM), Android |
+| Clean architecture folder structure | ✅ | domain / infrastructure / presentation layers |
+| `pubspec.yaml` configuration | ✅ | Drift, Equatable, flutter_bloc, uuid, meta, path_provider |
+| `analysis_options.yaml` lint rules | ✅ | very_good_analysis base + project overrides |
+| `Echelon` entity | ✅ | Brigade→Team hierarchy, `isValidParent`, `isValidChild`, `level` |
+| `DoctrinalBranch` entity | ✅ | Infantry, Medical, Fires, Signal, Cyber/EW, Aviation, Logistics, Engineer |
+| `AuthorizedRank` entity | ✅ | E1–E9, W1–W5, O1–O10; `payGrade`, `rankTitle`, `meetsMinimum`, category flags |
+| `UnitNode` entity | ✅ | Immutable, `Equatable`, `copyWith`, `isRoot`, `isValidParentNode` |
+| `PositionSlot` entity | ✅ | Immutable, `Equatable`, `copyWith`, `isVacant`, `isFilled` |
+| `PersonnelRecord` entity | ✅ | Immutable, `Equatable`, `copyWith`, `ReadinessState` (FMC/PMC/NMC) |
+| `DutyMos` value object | ✅ | Validated MOS codes, `@immutable`, `codesForBranch`, `isKnown` |
+| `domain_entities.dart` barrel export | ✅ | Single import for all domain entities |
+| `UnitRepository` abstract interface | ✅ | CRUD + subtree + watch streams |
+| `PersonnelRepository` abstract interface | ✅ | CRUD + assignment + watch streams |
+| `AppDatabase` (Drift) schema | ✅ | Units, PositionSlots, Personnel, Templates tables |
+| Drift code generation (`build_runner`) | ✅ | `app_database.g.dart` generated (94 KB) |
+| `assets/data/mos_dictionary.json` | ✅ | 8 branches, 60+ MOS codes, position slot templates |
+| `assets/svg/` directory | ✅ | Reserved for NATO military symbols (Phase 2) |
+| GitHub Actions CI/CD pipeline | ✅ | Test + analyze + build (Windows, Web WASM, Android) |
+| `lib/main.dart` placeholder scaffold | ✅ | Army-blue Material 3 theme, placeholder UI |
+| Unit tests — `Echelon` | ✅ | 17 tests passing |
+| Unit tests — `AuthorizedRank` | ✅ | 16 tests passing |
+| Unit tests — `PersonnelRecord` | ✅ | 13 tests passing |
+| Unit tests — `UnitNode` | ✅ | 15 tests passing |
+| Unit tests — `DutyMos` | ✅ | 32 tests passing |
+| Widget test — placeholder scaffold | ✅ | 1 test passing |
+| **Total tests** | ✅ | **94 / 94 passing** |
+| `flutter analyze` | ✅ | **No issues found** |
+
+---
+
+## Phase 2 — Canvas Engine & Drag-Drop UTO Builder (NEXT)
+
+### Planned Items
+
+| Item | Priority | Notes |
+|------|----------|-------|
+| `UnitTreeService` domain service | High | Build/validate unit tree from `UnitNode` list |
+| `AssignmentService` domain service | High | Assign/unassign soldiers to slots with validation |
+| Drift DAO implementations | High | `UnitDao`, `PersonnelDao`, `PositionSlotDao`, `TemplateDao` |
+| Repository concrete implementations | High | Wire DAOs to abstract repository interfaces |
+| `flutter_bloc` Cubits/Blocs | High | `UnitTreeCubit`, `PersonnelCubit`, `TemplateCubit` |
+| Three-pane UI shell | High | Left panel (unit tree), center canvas, right panel (personnel) |
+| Interactive canvas widget | High | Scrollable/zoomable org chart canvas |
+| Drag-and-drop soldier assignment | High | Drag from personnel panel → drop on position slot |
+| NATO military symbol SVGs | Medium | Brigade, Battalion, Company, Platoon, Squad, Team symbols |
+| Unit node card widget | Medium | Echelon symbol + name + fill rate indicator |
+| Position slot card widget | Medium | MOS badge + rank + soldier name or "VACANT" |
+| Readiness color coding | Medium | FMC=green, PMC=amber, NMC=red |
+| Template save/load | Medium | Serialize unit tree to JSON, persist via `Templates` table |
+
+---
+
+## Phase 3 — Reporting & Export (PLANNED)
+
+| Item | Priority | Notes |
+|------|----------|-------|
+| UTO report generation | High | PDF/HTML export of unit task organization |
+| Readiness dashboard | High | Aggregate FMC/PMC/NMC statistics by echelon |
+| Personnel roster export | Medium | CSV export of assigned/unassigned soldiers |
+| Template library | Medium | Save/load/share UTO templates |
+| Multi-BCT support | Low | Support multiple simultaneous BCT configurations |
+| Web deployment | Low | Deploy WASM build to internal web server |
+
+---
+
+## Known Issues / Technical Debt
+
+| Issue | Severity | Resolution |
+|-------|----------|-----------|
+| `lines_longer_than_80_chars` disabled | Low | Dart formatter produces lines > 80 chars for chained method calls; rule disabled in `analysis_options.yaml` |
+| Drift DAOs not yet implemented | Medium | Phase 2 work item — abstract interfaces defined, concrete implementations pending |
+| No integration tests | Medium | Phase 2 will add integration tests for repository implementations |
+| `sqlite3_flutter_libs` EOL warning | Low | Will upgrade when `drift` supports newer version |
+
+---
+
+## Test Coverage Summary
+
+```
+flutter test --coverage
+94 tests | 0 failures | 0 skipped
+Coverage: domain layer ~95%+ (entities, value objects)
+```
+
+---
+
+## Branch History
+
+| Branch | Merged | Description |
+|--------|--------|-------------|
+| `feature/phase-1-foundations` | 2026-08-07 | Phase 1: Architecture & Data Foundations |
diff --git a/lib/domain/entities/authorized_rank.dart b/lib/domain/entities/authorized_rank.dart
new file mode 100644
index 0000000..06202b1
--- /dev/null
+++ b/lib/domain/entities/authorized_rank.dart
@@ -0,0 +1,119 @@
+/// Represents authorized pay grades in the U.S. Army.
+///
+/// Covers Enlisted (E1–E9), Warrant Officer (W1–W5), and
+/// Commissioned Officer (O1–O10) grades.
+enum AuthorizedRank {
+ // Enlisted
+ e1,
+ e2,
+ e3,
+ e4,
+ e5,
+ e6,
+ e7,
+ e8,
+ e9,
+ // Warrant Officers
+ w1,
+ w2,
+ w3,
+ w4,
+ w5,
+ // Commissioned Officers
+ o1,
+ o2,
+ o3,
+ o4,
+ o5,
+ o6,
+ o7,
+ o8,
+ o9,
+ o10;
+
+ /// Returns the pay grade string (e.g., "E-4", "W-2", "O-3").
+ String get payGrade {
+ final name = this.name.toUpperCase();
+ return '${name[0]}-${name.substring(1)}';
+ }
+
+ /// Returns the common rank title for this pay grade.
+ String get rankTitle {
+ switch (this) {
+ case AuthorizedRank.e1:
+ return 'Private';
+ case AuthorizedRank.e2:
+ return 'Private Second Class';
+ case AuthorizedRank.e3:
+ return 'Private First Class';
+ case AuthorizedRank.e4:
+ return 'Specialist / Corporal';
+ case AuthorizedRank.e5:
+ return 'Sergeant';
+ case AuthorizedRank.e6:
+ return 'Staff Sergeant';
+ case AuthorizedRank.e7:
+ return 'Sergeant First Class';
+ case AuthorizedRank.e8:
+ return 'Master Sergeant / First Sergeant';
+ case AuthorizedRank.e9:
+ return 'Sergeant Major / Command Sergeant Major';
+ case AuthorizedRank.w1:
+ return 'Warrant Officer 1';
+ case AuthorizedRank.w2:
+ return 'Chief Warrant Officer 2';
+ case AuthorizedRank.w3:
+ return 'Chief Warrant Officer 3';
+ case AuthorizedRank.w4:
+ return 'Chief Warrant Officer 4';
+ case AuthorizedRank.w5:
+ return 'Chief Warrant Officer 5';
+ case AuthorizedRank.o1:
+ return 'Second Lieutenant';
+ case AuthorizedRank.o2:
+ return 'First Lieutenant';
+ case AuthorizedRank.o3:
+ return 'Captain';
+ case AuthorizedRank.o4:
+ return 'Major';
+ case AuthorizedRank.o5:
+ return 'Lieutenant Colonel';
+ case AuthorizedRank.o6:
+ return 'Colonel';
+ case AuthorizedRank.o7:
+ return 'Brigadier General';
+ case AuthorizedRank.o8:
+ return 'Major General';
+ case AuthorizedRank.o9:
+ return 'Lieutenant General';
+ case AuthorizedRank.o10:
+ return 'General';
+ }
+ }
+
+ /// Returns true if this rank is an enlisted grade.
+ bool get isEnlisted => name.startsWith('e');
+
+ /// Returns true if this rank is a warrant officer grade.
+ bool get isWarrantOfficer => name.startsWith('w');
+
+ /// Returns true if this rank is a commissioned officer grade.
+ bool get isCommissionedOfficer => name.startsWith('o');
+
+ /// Returns the numeric grade level (1–10 within each category).
+ int get gradeLevel => int.parse(name.substring(1));
+
+ /// Returns true if this rank meets or exceeds [minimum].
+ bool meetsMinimum(AuthorizedRank minimum) {
+ if (isEnlisted && minimum.isEnlisted) {
+ return gradeLevel >= minimum.gradeLevel;
+ }
+ if (isWarrantOfficer && minimum.isWarrantOfficer) {
+ return gradeLevel >= minimum.gradeLevel;
+ }
+ if (isCommissionedOfficer && minimum.isCommissionedOfficer) {
+ return gradeLevel >= minimum.gradeLevel;
+ }
+ return false;
+ }
+}
diff --git a/lib/domain/entities/doctrinal_branch.dart b/lib/domain/entities/doctrinal_branch.dart
new file mode 100644
index 0000000..7140ba5
--- /dev/null
+++ b/lib/domain/entities/doctrinal_branch.dart
@@ -0,0 +1,57 @@
+/// Represents the functional branch/specialty of a military unit or position.
+///
+/// Based on U.S. Army 2026 doctrine functional branch designations.
+enum DoctrinalBranch {
+ infantry,
+ medical,
+ fires,
+ signal,
+ cyberEw,
+ aviation,
+ logistics,
+ engineer;
+
+ /// Returns a human-readable display name for this branch.
+ String get displayName {
+ switch (this) {
+ case DoctrinalBranch.infantry:
+ return 'Infantry & Maneuver';
+ case DoctrinalBranch.medical:
+ return 'Medical (CMF 68)';
+ case DoctrinalBranch.fires:
+ return 'Field Artillery (CMF 13)';
+ case DoctrinalBranch.signal:
+ return 'Signal (CMF 25)';
+ case DoctrinalBranch.cyberEw:
+ return 'Cyber/EW (CMF 17)';
+ case DoctrinalBranch.aviation:
+ return 'Aviation';
+ case DoctrinalBranch.logistics:
+ return 'Logistics (CMF 91/92)';
+ case DoctrinalBranch.engineer:
+ return 'Engineer (CMF 12)';
+ }
+ }
+
+ /// Returns the short branch abbreviation.
+ String get abbreviation {
+ switch (this) {
+ case DoctrinalBranch.infantry:
+ return 'IN';
+ case DoctrinalBranch.medical:
+ return 'MC';
+ case DoctrinalBranch.fires:
+ return 'FA';
+ case DoctrinalBranch.signal:
+ return 'SC';
+ case DoctrinalBranch.cyberEw:
+ return 'CY';
+ case DoctrinalBranch.aviation:
+ return 'AV';
+ case DoctrinalBranch.logistics:
+ return 'LG';
+ case DoctrinalBranch.engineer:
+ return 'EN';
+ }
+ }
+}
diff --git a/lib/domain/entities/domain_entities.dart b/lib/domain/entities/domain_entities.dart
new file mode 100644
index 0000000..59225ea
--- /dev/null
+++ b/lib/domain/entities/domain_entities.dart
@@ -0,0 +1,11 @@
+/// Domain entities barrel export.
+///
+/// Import this file to access all domain entity types.
+library;
+
+export 'authorized_rank.dart';
+export 'doctrinal_branch.dart';
+export 'echelon.dart';
+export 'personnel_record.dart';
+export 'position_slot.dart';
+export 'unit_node.dart';
diff --git a/lib/domain/entities/echelon.dart b/lib/domain/entities/echelon.dart
new file mode 100644
index 0000000..e5610aa
--- /dev/null
+++ b/lib/domain/entities/echelon.dart
@@ -0,0 +1,64 @@
+/// Represents the hierarchical echelon levels in U.S. Army doctrine.
+///
+/// Ordered from highest (Brigade) to lowest (Team) echelon.
+enum Echelon {
+ brigade,
+ battalion,
+ company,
+ platoon,
+ section,
+ squad,
+ team;
+
+ /// Returns a human-readable display name for this echelon.
+ String get displayName {
+ switch (this) {
+ case Echelon.brigade:
+ return 'Brigade';
+ case Echelon.battalion:
+ return 'Battalion';
+ case Echelon.company:
+ return 'Company';
+ case Echelon.platoon:
+ return 'Platoon';
+ case Echelon.section:
+ return 'Section';
+ case Echelon.squad:
+ return 'Squad';
+ case Echelon.team:
+ return 'Team';
+ }
+ }
+
+ /// Returns the numeric level (lower = higher echelon).
+ int get level {
+ switch (this) {
+ case Echelon.brigade:
+ return 1;
+ case Echelon.battalion:
+ return 2;
+ case Echelon.company:
+ return 3;
+ case Echelon.platoon:
+ return 4;
+ case Echelon.section:
+ return 5;
+ case Echelon.squad:
+ return 6;
+ case Echelon.team:
+ return 7;
+ }
+ }
+
+ /// Returns true if [other] is a valid parent echelon for this echelon.
+ ///
+ /// A valid parent must be exactly one level higher (lower level number).
+ bool isValidParent(Echelon other) {
+ return other.level == level - 1;
+ }
+
+ /// Returns true if [other] is a valid child echelon for this echelon.
+ bool isValidChild(Echelon other) {
+ return other.level == level + 1;
+ }
+}
diff --git a/lib/domain/entities/personnel_record.dart b/lib/domain/entities/personnel_record.dart
new file mode 100644
index 0000000..20d5150
--- /dev/null
+++ b/lib/domain/entities/personnel_record.dart
@@ -0,0 +1,117 @@
+import 'package:equatable/equatable.dart';
+
+import 'package:uto_manager/domain/entities/authorized_rank.dart';
+import 'package:uto_manager/domain/value_objects/duty_mos.dart';
+
+/// Represents the readiness state of a soldier.
+enum ReadinessState {
+ /// Fully mission-capable; no limiting factors.
+ fullyMissionCapable,
+
+ /// Partially mission-capable; some limiting factors present.
+ partiallyMissionCapable,
+
+ /// Not mission-capable; significant limiting factors.
+ notMissionCapable,
+
+ /// Status unknown or not yet assessed.
+ unknown;
+
+ /// Returns a human-readable display name.
+ String get displayName {
+ switch (this) {
+ case ReadinessState.fullyMissionCapable:
+ return 'FMC';
+ case ReadinessState.partiallyMissionCapable:
+ return 'PMC';
+ case ReadinessState.notMissionCapable:
+ return 'NMC';
+ case ReadinessState.unknown:
+ return 'Unknown';
+ }
+ }
+}
+
+/// Represents a soldier's personnel record in the UTO system.
+///
+/// Contains identifying information, MOS qualifications, pay grade,
+/// and current readiness state.
+class PersonnelRecord extends Equatable {
+ const PersonnelRecord({
+ required this.soldierId,
+ required this.name,
+ required this.primaryMos,
+ required this.payGrade,
+ required this.readinessState,
+ this.qualifications = const [],
+ this.assignedSlotId,
+ });
+
+ /// Unique identifier for this soldier (UUID v4).
+ final String soldierId;
+
+ /// The soldier's full name (Last, First MI format).
+ final String name;
+
+ /// The soldier's primary Military Occupational Specialty.
+ final DutyMos primaryMos;
+
+ /// The soldier's current pay grade.
+ final AuthorizedRank payGrade;
+
+ /// Additional MOS qualifications and special skills.
+ final List qualifications;
+
+ /// The current readiness state of this soldier.
+ final ReadinessState readinessState;
+
+ /// The ID of the position slot this soldier is assigned to, or null.
+ final String? assignedSlotId;
+
+ /// Returns true if this soldier is currently assigned to a position.
+ bool get isAssigned => assignedSlotId != null;
+
+ /// Returns true if this soldier is available for assignment.
+ bool get isUnassigned => assignedSlotId == null;
+
+ /// Returns true if this soldier is fully mission capable.
+ bool get isFmc => readinessState == ReadinessState.fullyMissionCapable;
+
+ /// Returns a copy of this [PersonnelRecord] with the given fields replaced.
+ PersonnelRecord copyWith({
+ String? soldierId,
+ String? name,
+ DutyMos? primaryMos,
+ AuthorizedRank? payGrade,
+ List? qualifications,
+ ReadinessState? readinessState,
+ String? assignedSlotId,
+ bool clearAssignment = false,
+ }) {
+ return PersonnelRecord(
+ soldierId: soldierId ?? this.soldierId,
+ name: name ?? this.name,
+ primaryMos: primaryMos ?? this.primaryMos,
+ payGrade: payGrade ?? this.payGrade,
+ qualifications: qualifications ?? this.qualifications,
+ readinessState: readinessState ?? this.readinessState,
+ assignedSlotId: clearAssignment ? null : (assignedSlotId ?? this.assignedSlotId),
+ );
+ }
+
+ @override
+ List