uto_manager/development_status.md

402 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UTO Manager — Development Status
Last updated: 2026-08-09 — Phase 5 UI/CRUD layer merged to `main`
---
## Current Branch
`main` (Phase 5 UI/CRUD layer merged)
## Overall Progress
| Phase | Title | Status |
|-------|-------|--------|
| 1 | Domain Foundation | ✅ Complete |
| 2 | Canvas Engine & Persistence | ✅ Complete |
| 3 | Template Library & Import/Export | ✅ Complete |
| 4 | Reporting & Analytics | ✅ Complete |
| 5 | UI/CRUD Layer & Web Platform | ✅ Complete |
---
## Phase 1 — Domain Foundation ✅
**Completed items:**
- `Echelon` entity with doctrinal hierarchy validation (`isValidParent`, `isValidChild`)
- `DoctrinalBranch` entity with display names
- `AuthorizedRank` entity with pay grade, category flags, `meetsMinimum`, `gradeLevel`
- `DutyMos` value object with MOS dictionary validation and `unvalidated` constructor
- `UnitNode` entity with `copyWith`, `isRoot`, `isValidParentNode`
- `PositionSlot` entity with `isVacant`, `isFilled`, `isKeyLeader`
- `PersonnelRecord` entity with `ReadinessState`, `copyWith`, `isUnassigned`
- `UnitRepository` abstract interface
- `PersonnelRepository` abstract interface
- `domain_entities.dart` barrel export
- Full unit test coverage for all domain entities and value objects
**Test count (Phase 1):** 60 tests
---
## Phase 2 — Canvas Engine & Persistence ✅
**Completed items:**
### Domain Services (Phase 2)
- `UnitTreeService` — `buildTree`, `getSubtree`, `getAncestors`, `validateReparent`,
`validateAssignment`, `calculateFillRate`, `getVacantKeyLeaderSlots`, `getMosMismatches`
- `AssignmentService` — `assign`, `unassign`, `aggregateReadiness` with `ReadinessColor`
### Infrastructure — Database
- Drift schema: `Units`, `PositionSlots`, `Personnel`, `Templates` tables
- `AppDatabase` with `forTesting(NativeDatabase.memory())` constructor
- `UnitDao` — full CRUD + watch streams
- `PersonnelDao` — full CRUD + `assignToSlot` / `unassignFromSlot` + watch streams
- `PositionSlotDao` — full CRUD + `assignPersonnel` / `clearAssignment` + watch streams
- `TemplateDao` — full CRUD + `upsertTemplate`
- build_runner code generation (`.g.dart` files)
### Infrastructure — Repositories
- `DriftUnitRepository` — implements `UnitRepository` with domain↔Drift mapping
- `DriftPersonnelRepository` — implements `PersonnelRepository` with domain↔Drift mapping
- `DriftPositionSlotRepository` — concrete slot repository
### Application — BLoC (Phase 2)
- `UnitTreeCubit` / `UnitTreeState` (sealed) — tree loading, selection, reparent, subtree delete
- `PersonnelCubit` / `PersonnelState` (sealed) — roster loading, filter, assign/unassign
- `TemplateCubit` / `TemplateState` (sealed) — template save/load/delete
### Presentation (Phase 2)
- `HomeScreen` — three-pane adaptive layout (personnel | canvas | inspector)
- `UtoCanvasWidget` — `InteractiveViewer` pan/zoom org chart with drag-and-drop reparent
- `UnitNodeCard` — echelon-colored card with fill rate progress bar
- `PositionSlotCard` — `DragTarget` slot card with MOS badge and readiness color
- `PersonnelListTile` — `Draggable` soldier tile for assignment
- Status bar with live unit/slot fill counts
### App Wiring (Phase 2)
- `main.dart` — `MultiBlocProvider` wiring all cubits with production database
**Test count (Phase 2):** 77 new tests (137 total)
---
## Phase 3 — Template Library & Import/Export ✅
**Completed items:**
### Domain Services (Phase 3)
- `DoctrinalTemplates` — Pure-Dart library of all five 2026 BCT templates:
- `mbct` — Mobile Brigade Combat Team (3× Mobile Infantry Battalions, Cav Squadron, FA Battalion, MDEP, HKP, Engineer Battalion, Support Battalion)
- `sbct` — Stryker Brigade Combat Team (3× Stryker Infantry Battalions, Cav Recon Squadron, FA Battalion, Engineer Battalion, Support Battalion)
- `abct` — Armored Brigade Combat Team (2× Armor-Heavy CABs, 1× Infantry-Heavy CAB, Armored Cav Squadron, M109A7 FA Battalion, Engineer Battalion)
- `cabHeavy` — Combat Aviation Brigade Heavy (Attack Bn AH-64E, Assault Bn UH-60M/CH-47F, Air Ambulance Co, Aviation Support Bn)
- `cabLight` — Combat Aviation Brigade Light (Attack Recon Bn AH-64E+MQ-9A, Assault Bn, Medical Assault Bn, Aviation Support Bn)
- `TemplateInstantiator` — Converts `UnitDefinition` trees into concrete `UnitNode` + `PositionSlot` domain objects with injected ID generator
- `MtoeValidationEngine` — Pure-domain validation engine with 7 rules:
- **V001** MOS mismatch (warning)
- **V002** Rank mismatch (warning)
- **V003** Vacant key leader (critical)
- **V004** Missing combat medic 68W in platoon (advisory)
- **V005** Missing fire support specialist 13F in platoon (advisory)
- **V006** Structural hierarchy violation (critical)
- **V007** Manning below 70% (warning)
### Infrastructure — Codecs (Phase 3)
- `UnitTreeCodec` — JSON encode/decode for full unit tree snapshots (units + slots + personnel)
- Schema version 1 with `exportedAt` timestamp
- `encode` / `decode` (JSON string)
- `encodeToMap` / `decodeFromMap` (Map)
- Handles all optional fields (null parentUnitId, null assignedPersonnelId, qualifications list)
### Application — BLoC (Phase 3)
- `TemplateCubit` — Extended with:
- `instantiateDoctrinalTemplate(key)` — instantiates any of the five BCT templates into the live database
- `saveSnapshot(name, bctType, units, slots)` — saves current tree as a named template
- `exportToJson(units, slots)` — encodes tree to JSON string for export
- `importFromJson(json, {replaceExisting})` — decodes and persists imported tree
- `ValidationCubit` / `ValidationState` (sealed) — wraps `MtoeValidationEngine`:
- `validate(units, slots, personnel)` — runs all rules, emits `ValidationComplete`
- `clear()` — resets to `ValidationInitial`
- `ValidationComplete` exposes `.critical`, `.warnings`, `.advisories`, `.findingsForUnit(id)`, `.isClean`
### Domain Value Objects (Phase 3)
- `DutyMos` — Extended format validation to accept 4-character aviation AOC codes (e.g., `153A` = 3 digits + 1 letter) in addition to standard 3-character codes
### App Wiring (Phase 3)
- `main.dart` — Added `ValidationCubit` to `MultiBlocProvider`
- `TemplateCubit` — Now requires `unitRepository` and `slotRepository` parameters
**Test count (Phase 3):** 54 new tests (191 total)
---
## Phase 4 — Reporting & Analytics ✅
**Completed items:**
### Domain Services (Phase 4)
- `CommandEventStore` — In-memory append-only event log for all tree mutations:
- `CommandEvent` with `eventId`, `type`, `payload`, `occurredAt`, `isSynced`
- `CommandEventType` enum: `attachNode`, `detachNode`, `assignPersonnel`, `unassignPersonnel`, `modifySlot`
- `CommandPayloads` factory helpers for each event type
- `append`, `markSynced`, `markAllSynced`, `exportPendingToJson`, `clear`
- Unmodifiable `allEvents` and `pendingEvents` views
- `DeltaSynchronizer` — Offline-first sync helper:
- `syncIfOnline()` — transmits pending events when online, returns count synced
- `isOnline` getter/setter for connectivity simulation
- `IsolateValidationRunner` — Background-isolate wrapper for `MtoeValidationEngine`:
- `start()` / `dispose()` lifecycle management (idempotent start)
- `validate(units, slots, personnel)` — async, runs engine off the main thread
- `ValidationResponse` with `findings`, `hasError`, optional `error` string
- Results guaranteed to match synchronous engine output
### Infrastructure — Codecs (Phase 4)
- `ProtobufCodec` — Binary encode/decode for unit tree snapshots:
- 12-byte header: magic `UTOB` (0x55544F42) + version 1 (big-endian uint32)
- Payload: zlib-compressed JSON (reuses `UnitTreeCodec` for serialization)
- `encode(units, slots, personnel)` → `Uint8List`
- `decode(bytes)` → `UnitTreeSnapshot` (validates magic + version)
- `compressionRatio(binaryBytes, jsonBytes)` static helper
- Produces output smaller than raw JSON for non-trivial trees
- Throws `FormatException` for truncated data, wrong magic, or unsupported version
- `PdfChartExporter` — Pure-Dart PDF org-chart generator (no external PDF library):
- `export(units, slots, unitDesignation, {pageSize, exportDate})` → `List<int>`
- Generates valid PDF 1.4 with xref table, trailer, `%%EOF`
- Cover page with unit designation, export date, UNCLASSIFIED banner
- Org-chart page with unit boxes, connector lines (`m`/`l S` operators)
- Supports `PdfPageSize.letter` (612×792), `a4` (595×842), `tabloid` (792×1224)
- Unit names truncated to 18 chars; all echelon levels render without error
### Application — BLoC (Phase 4)
- `ValidationCubit` — Extended with isolate-backed validation:
- `startIsolate()` / `disposeIsolate()` lifecycle hooks
- `validateAsync(units, slots, personnel)` — delegates to `IsolateValidationRunner`
- Falls back to synchronous engine if isolate not started
- `ValidationState` extended with `isRunning` flag
### Presentation (Phase 4)
- `HomeScreen` — Updated with Phase 4 UI:
- Validation node indicators on canvas: red border for critical findings, amber for warnings
- "Export PDF" action in app bar → calls `PdfChartExporter`, triggers file save dialog
- "Export Binary" action → calls `ProtobufCodec`, saves `.utob` file
- Validation findings panel in inspector pane (grouped by severity)
- Status bar shows pending sync event count
**Test count (Phase 4):** 72 new tests (263 total)
---
## Phase 5 — UI/CRUD Layer & Web Platform ✅
**Completed items:**
### Domain Entities (Phase 5)
- `SoldierPosition` entity — named duty position for assignment to personnel records:
- `positionId` (UUID v4 for custom; fixed string for built-ins)
- `title` — human-readable position title (e.g. "Team Leader", "Rifleman")
- `isBuiltIn` — true for app-seeded positions; false for user-created ones
- `copyWith`, `Equatable` props, `toString`
- `SoldierPosition.builtIns` — 31 pre-defined Army duty positions across all branches:
- Command/Staff: Commander, XO, CSM, First Sergeant, S2/S3/S4/S6
- Platoon: Platoon Leader, Platoon Sergeant, RTO
- Squad/Section: Squad Leader, Team Leader
- Individual: Rifleman, Grenadier, Automatic Rifleman, Machine Gunner, Sniper,
JTAC/Fire Support, Medic, Combat Engineer, Driver, Gunner, Loader,
Tank Commander, Crew Member, Supply Sergeant, Armorer, Commo Sergeant,
Intelligence Analyst, Forward Observer, CBRN Specialist
### Domain Entities — Extended (Phase 5)
- `PersonnelRecord` — Extended with:
- `positionTitle` field (optional) — duty position title from `SoldierPosition`
- `displayLabel` getter — prefers `name`; falls back to `positionTitle`; then `(Unnamed Soldier)`
- `isAssigned` getter — true when `assignedSlotId != null`
- `isFmc` getter — true when `readinessState == fullyMissionCapable`
- `copyWith` — extended with `clearName`, `clearPositionTitle`, `clearAssignment` flags
### Infrastructure — Database (Phase 5)
- `SoldierPositions` Drift table — persists named duty positions:
- `position_id` (TEXT, PK), `title` (TEXT), `is_built_in` (BOOLEAN, default false)
- `SoldierPositionsDao` — full DAO for `SoldierPositions`:
- `getAll()`, `getById(positionId)`, `upsert()`, `upsertAll()`, `updatePosition()`,
`deletePosition()`, `watchAll()`
- `AppDatabase` — schema version bumped to **v2**:
- Migration v1→v2: adds `position_title` column to `Personnel` table; creates `soldier_positions` table
- Web platform support:
- `drift_worker.dart` / `drift_worker.js` — Drift web worker for IndexedDB persistence
- `sqlite3.wasm` — SQLite WASM binary for web target
- `_openConnection()` updated to use `driftDatabase()` (platform-adaptive: SQLite on native, WASM/IndexedDB on web)
### Infrastructure — Repositories (Phase 5)
- `DriftSoldierPositionsRepository` — Drift-backed repository for `SoldierPosition`:
- `getAll()` — returns all positions (built-in + custom)
- `watchAll()` — reactive stream of all positions
- `upsert(position)` — insert or update a position
- `seedBuiltIns()` — idempotent seed of all `SoldierPosition.builtIns` on first launch
- `delete(positionId)` — deletes custom positions only; no-op for built-ins
### Application — BLoC (Phase 5)
- `UnitTreeCubit` — Extended with full CRUD operations:
- `addUnit(unit)` — inserts a new `UnitNode`
- `updateUnit(unit)` — updates an existing `UnitNode`
- `addSlot(slot)` — inserts a new `PositionSlot`
- `updateSlot(slot)` — updates an existing `PositionSlot`
- `deleteSlot(slotId)` — deletes a position slot
- `clearAll()` — deletes all slots then all units (template reset)
- `PersonnelCubit` — Extended with full CRUD operations:
- `addPersonnel(record)` — inserts a new `PersonnelRecord`
- `updatePersonnel(record)` — updates an existing `PersonnelRecord`
- `deletePersonnel(soldierId)` — deletes soldier; clears slot assignment first if assigned
### Presentation — Dialogs (Phase 5)
- `AddEditPersonnelDialog` — full-featured dialog for creating/editing soldiers:
- Position dropdown (loaded from `DriftSoldierPositionsRepository`) with add/rename/delete custom positions
- Optional full name field (Last, First MI format)
- Pay grade dropdown (all `AuthorizedRank` values)
- Primary MOS field with live autocomplete suggestions from `DutyMos.allCodes`
- Readiness state dropdown
- MOS validation against known codes
- `AddEditSlotDialog` — dialog for creating/editing position slots:
- Position title text field
- Authorized rank dropdown
- Duty MOS field with live autocomplete suggestions
- Key leader toggle switch
- `AddEditUnitDialog` — dialog for creating/editing unit nodes:
- Optional unit name/designation field (auto-generates from echelon + branch if blank)
- Echelon dropdown
- Branch/functional area dropdown
- Optional parent unit dropdown (excludes self and descendants)
- Key leader unit toggle switch
### Presentation — HomeScreen (Phase 5)
- App bar actions:
- **Add Unit** (`add_box_outlined`) — opens `AddEditUnitDialog`
- **Add Soldier** (`person_add_outlined`) — opens `AddEditPersonnelDialog`
- **Run MTOE Validation** (`fact_check_outlined`) — triggers `ValidationCubit.validate()`
- **Export** (`upload_file_outlined`) — opens export format selection dialog
- **Clear All** (`delete_sweep_outlined`) — confirmation dialog to reset all units/slots
- Personnel panel:
- Search/filter bar (name, MOS, rank)
- Tap soldier tile to edit via `AddEditPersonnelDialog`
- Assigned/unassigned count footer
- Empty-state prompt with "Add Soldier" button
- Inspector panel:
- Edit unit button → `AddEditUnitDialog`
- Add child unit button → `AddEditUnitDialog` (pre-selected parent)
- Delete unit button → confirmation dialog (cascades to subtree)
- Validation findings section (per-unit, grouped by severity with color-coded icons)
- Position slots header with fill count and "Add Slot" button
- Per-slot edit and delete actions via `AddEditSlotDialog`
- Empty-state prompts for no units selected and no slots defined
- Export dialog (`_ExportDialog`):
- Format selection: JSON, Binary (`.utob`), PDF
- PDF page size selector (Letter, A4, Tabloid)
- Export result/error feedback inline
- Status bar:
- Unit count, slot fill rate (filled/total + percentage)
- Total soldier count
- Validation state: running spinner, clean indicator, or critical/warning/advisory counts
### App Wiring (Phase 5)
- `main.dart` — Added `DriftSoldierPositionsRepository` wired to `HomeScreen`
- `HomeScreen` — now requires `positionsRepository` parameter
**Test count (Phase 5):** existing 263 tests; widget test updated for new `HomeScreen` signature
---
## Test Summary
| File | Tests | Status |
|------|-------|--------|
| `authorized_rank_test.dart` | 16 | ✅ |
| `echelon_test.dart` | 17 | ✅ |
| `personnel_record_test.dart` | 13 | ✅ |
| `unit_node_test.dart` | 15 | ✅ |
| `duty_mos_test.dart` | 32 | ✅ |
| `assignment_service_test.dart` | 16 | ✅ |
| `unit_tree_service_test.dart` | 27 | ✅ |
| `mtoe_validation_engine_test.dart` | 22 | ✅ |
| `template_instantiator_test.dart` | 18 | ✅ |
| `unit_tree_codec_test.dart` | 14 | ✅ |
| `command_event_store_test.dart` | 25 | ✅ |
| `validation_isolate_test.dart` | 14 | ✅ |
| `protobuf_codec_test.dart` | 18 | ✅ |
| `pdf_chart_exporter_test.dart` | 19 | ✅ |
| `widget_test.dart` | 1 | ✅ |
| **Total** | **263** | **✅ All pass** |
---
## Known Limitations / Next Steps (Phase 5 Hardening)
- No multi-user or real-time sync capability (Phase 5 hardening)
- Canvas layout is vertical tree only; horizontal layout not yet implemented
- No undo/redo for assignment operations
- No search/filter on the canvas itself
- `DeltaSynchronizer` uses simulated online/offline; real network transport deferred
- `ProtobufCodec` uses zlib-compressed JSON as payload; true protobuf schema deferred
- Template instantiation does not yet persist to database in a single Drift transaction (uses sequential inserts)
- MIL-STD-2525D symbology engine not yet implemented (deferred)
- SQLCipher data-at-rest encryption not yet integrated (deferred)
- No undo/redo stack for unit/slot/personnel CRUD operations
---
## Architecture Notes
```text
lib/
├── domain/
│ ├── entities/ # Pure Dart domain models (no Flutter deps)
│ │ # UnitNode, PositionSlot, PersonnelRecord, SoldierPosition
│ ├── value_objects/ # DutyMos (supports 3-char and 4-char AOC formats)
│ ├── repositories/ # Abstract interfaces
│ └── services/ # UnitTreeService, AssignmentService,
│ # MtoeValidationEngine, TemplateInstantiator,
│ # DoctrinalTemplates, CommandEventStore,
│ # DeltaSynchronizer, IsolateValidationRunner
├── infrastructure/
│ ├── codecs/ # UnitTreeCodec (JSON), ProtobufCodec (binary),
│ │ # PdfChartExporter (PDF org chart)
│ ├── database/ # Drift schema (v2), DAOs, generated code
│ │ # Tables: Units, PositionSlots, Personnel,
│ │ # SoldierPositions, Templates
│ └── repositories/ # Drift-backed implementations
│ # DriftSoldierPositionsRepository (new in Phase 5)
├── application/
│ └── blocs/ # UnitTreeCubit, PersonnelCubit,
│ # TemplateCubit, ValidationCubit
└── presentation/
├── screens/
│ ├── home_screen.dart # Full CRUD UI + export dialog
│ └── dialogs/ # AddEditPersonnelDialog, AddEditSlotDialog,
│ # AddEditUnitDialog (all new in Phase 5)
└── widgets/ # Canvas, cards, tiles
```