diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 1468f3d..9f725b3 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -10,7 +10,7 @@ Requirements for initial release. Each maps to roadmap phases. ### Foundation - [x] **FOUND-01**: Application built with C#/WPF (.NET 10 LTS) using MVVM architecture -- [ ] **FOUND-02**: Multi-tenant profile registry — user can create, rename, delete, and switch between tenant profiles (tenant URL, client ID, display name) +- [x] **FOUND-02**: Multi-tenant profile registry — user can create, rename, delete, and switch between tenant profiles (tenant URL, client ID, display name) - [ ] **FOUND-03**: Multi-tenant session caching — user stays authenticated across tenant switches without re-logging in (MSAL token cache per tenant) - [ ] **FOUND-04**: Interactive Azure AD OAuth login via browser — no client secrets or certificates stored - [x] **FOUND-05**: All long-running operations report progress to the UI in real-time @@ -18,9 +18,9 @@ Requirements for initial release. Each maps to roadmap phases. - [x] **FOUND-07**: All errors surface to the user with actionable messages — no silent failures - [x] **FOUND-08**: Structured logging for diagnostics (Serilog or equivalent) - [ ] **FOUND-09**: Localization system supporting English and French with dynamic language switching -- [ ] **FOUND-10**: JSON-based local storage for profiles, settings, and templates (compatible with current app's format for migration) +- [x] **FOUND-10**: JSON-based local storage for profiles, settings, and templates (compatible with current app's format for migration) - [ ] **FOUND-11**: Self-contained single EXE distribution — no .NET runtime dependency for end users -- [ ] **FOUND-12**: Configurable data output folder for exports +- [x] **FOUND-12**: Configurable data output folder for exports ### Permissions @@ -116,7 +116,7 @@ Which phases cover which requirements. Updated during roadmap creation. | Requirement | Phase | Status | |-------------|-------|--------| | FOUND-01 | Phase 1 | Complete | -| FOUND-02 | Phase 1 | Pending | +| FOUND-02 | Phase 1 | Complete | | FOUND-03 | Phase 1 | Pending | | FOUND-04 | Phase 1 | Pending | | FOUND-05 | Phase 1 | Complete | @@ -124,9 +124,9 @@ Which phases cover which requirements. Updated during roadmap creation. | FOUND-07 | Phase 1 | Complete | | FOUND-08 | Phase 1 | Complete | | FOUND-09 | Phase 1 | Pending | -| FOUND-10 | Phase 1 | Pending | +| FOUND-10 | Phase 1 | Complete | | FOUND-11 | Phase 5 | Pending | -| FOUND-12 | Phase 1 | Pending | +| FOUND-12 | Phase 1 | Complete | | PERM-01 | Phase 2 | Pending | | PERM-02 | Phase 2 | Pending | | PERM-03 | Phase 2 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 176e126..b9b7494 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -104,7 +104,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 1. Foundation | 2/8 | In Progress| | +| 1. Foundation | 3/8 | In Progress| | | 2. Permissions | 0/? | Not started | - | | 3. Storage and File Operations | 0/? | Not started | - | | 4. Bulk Operations and Provisioning | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index e57eca7..d894196 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,14 +3,14 @@ gsd_state_version: 1.0 milestone: v1.0 milestone_name: milestone status: planning -stopped_at: Completed 01-foundation-02-PLAN.md -last_updated: "2026-04-02T10:07:59.501Z" +stopped_at: Completed 01-foundation-03-PLAN.md +last_updated: "2026-04-02T10:13:20.259Z" last_activity: 2026-04-02 — Roadmap created, requirements mapped, all 42 v1 requirements assigned to phases progress: total_phases: 5 completed_phases: 0 total_plans: 8 - completed_plans: 2 + completed_plans: 3 percent: 13 --- @@ -52,6 +52,7 @@ Progress: [█░░░░░░░░░] 13% *Updated after each plan completion* | Phase 01-foundation P01 | 4 | 2 tasks | 14 files | | Phase 01-foundation P02 | 1 | 2 tasks | 7 files | +| Phase 01-foundation P03 | 8 | 2 tasks | 7 files | ## Accumulated Context @@ -69,6 +70,9 @@ Recent decisions affecting current work: - [Phase 01-foundation]: Solution uses .slnx format (new .NET 10 XML solution) — dotnet new sln creates .slnx by default in .NET 10 SDK - [Phase 01-foundation]: TenantProfile is a plain mutable class (not record) — System.Text.Json requires settable properties; field names Name/TenantUrl/ClientId match JSON schema exactly - [Phase 01-foundation]: SharePointPaginationHelper uses [EnumeratorCancellation] on ct — required for correct WithCancellation() forwarding in async iterators +- [Phase 01-foundation]: Explicit System.IO using required in WPF project — WPF temp build project does not include System.IO in implicit usings; all persistence classes need explicit import +- [Phase 01-foundation]: SettingsService validates only 'en' and 'fr' language codes — throws ArgumentException for unsupported codes +- [Phase 01-foundation]: LoadAsync on corrupt JSON throws InvalidDataException (not silent empty) — explicit failure protects against silent data loss ### Pending Todos @@ -82,6 +86,6 @@ None yet. ## Session Continuity -Last session: 2026-04-02T10:07:59.499Z -Stopped at: Completed 01-foundation-02-PLAN.md +Last session: 2026-04-02T10:13:20.255Z +Stopped at: Completed 01-foundation-03-PLAN.md Resume file: None diff --git a/.planning/phases/01-foundation/01-03-SUMMARY.md b/.planning/phases/01-foundation/01-03-SUMMARY.md new file mode 100644 index 0000000..6eca6ad --- /dev/null +++ b/.planning/phases/01-foundation/01-03-SUMMARY.md @@ -0,0 +1,142 @@ +--- +phase: 01-foundation +plan: 03 +subsystem: persistence +tags: [dotnet10, csharp, system-text-json, semaphoreslim, write-then-replace, unit-tests, xunit] + +# Dependency graph +requires: + - 01-01 (solution scaffold, test project) + - 01-02 (TenantProfile model) +provides: + - ProfileRepository: file I/O for profiles JSON with SemaphoreSlim write lock and write-then-replace + - ProfileService: CRUD (GetProfiles/AddProfile/RenameProfile/DeleteProfile) with input validation + - SettingsRepository: file I/O for settings JSON with same write-then-replace safety pattern + - SettingsService: GetSettings/SetLanguage/SetDataFolder with supported-language validation + - AppSettings model: DataFolder + Lang with camelCase JSON compatibility +affects: + - 01-04 (MsalClientFactory may use ProfileService for tenant list) + - 01-05 (TranslationSource uses SettingsService for lang) + - 01-06 (FeatureViewModelBase may use ProfileService/SettingsService) + - all feature plans (profile and settings are the core data contracts) + +# Tech tracking +tech-stack: + added: [] + patterns: + - Write-then-replace: write to .tmp, validate JSON round-trip via JsonDocument.Parse, then File.Move(overwrite:true) + - SemaphoreSlim(1,1) for async exclusive write access on per-repository basis + - System.Text.Json with PropertyNamingPolicy.CamelCase for schema-compatible serialization + - PropertyNameCaseInsensitive=true for deserialization to handle both old and new JSON + - TDD with IDisposable temp file pattern for isolated unit tests + +key-files: + created: + - SharepointToolbox/Core/Models/AppSettings.cs + - SharepointToolbox/Infrastructure/Persistence/ProfileRepository.cs + - SharepointToolbox/Infrastructure/Persistence/SettingsRepository.cs + - SharepointToolbox/Services/ProfileService.cs + - SharepointToolbox/Services/SettingsService.cs + - SharepointToolbox.Tests/Services/ProfileServiceTests.cs + - SharepointToolbox.Tests/Services/SettingsServiceTests.cs + modified: [] + +key-decisions: + - "Explicit System.IO using required in WPF project — WPF temp build project does not include System.IO in implicit usings; all file I/O classes need explicit namespace import" + - "SettingsService validates only 'en' and 'fr' — matches app's supported locales; throws ArgumentException for any other code" + - "LoadAsync on corrupt JSON throws InvalidDataException (not silent empty) — explicit failure is safer than silently discarding user data" + +patterns-established: + - "Write-then-replace: all file persistence uses .tmp write + JsonDocument.Parse validation + File.Move(overwrite:true) to protect against crash-corruption" + - "IDisposable test pattern: unit tests use Path.GetTempFileName() + Dispose() for clean isolated file I/O tests" + +requirements-completed: + - FOUND-02 + - FOUND-10 + - FOUND-12 + +# Metrics +duration: 8min +completed: 2026-04-02 +--- + +# Phase 1 Plan 03: Persistence Layer Summary + +**ProfileRepository + SettingsRepository with write-then-replace safety, ProfileService + SettingsService with validation, 18 unit tests covering round-trips, corrupt-file recovery, concurrency, and JSON schema compatibility** + +## Performance + +- **Duration:** 8 min +- **Started:** 2026-04-02T10:09:13Z +- **Completed:** 2026-04-02T10:17:00Z +- **Tasks:** 2 +- **Files modified:** 7 + +## Accomplishments + +- ProfileRepository and SettingsRepository both implement write-then-replace (tmp file → JSON validation → File.Move) with SemaphoreSlim(1,1) preventing concurrent write corruption +- JSON serialization uses camelCase (PropertyNamingPolicy.CamelCase) — preserves existing user data field names: `profiles`, `name`, `tenantUrl`, `clientId`, `dataFolder`, `lang` +- ProfileService provides full CRUD with input validation (Name not empty, TenantUrl valid absolute URL, ClientId not empty) +- SettingsService validates language codes against supported set (en/fr only), allows empty dataFolder +- All 18 unit tests pass (10 ProfileServiceTests + 8 SettingsServiceTests); no skips + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: ProfileRepository and ProfileService with write-then-replace** - `769196d` (feat) +2. **Task 2: SettingsRepository and SettingsService** - `ac3fa5c` (feat) + +**Plan metadata:** (docs commit follows) + +## Files Created/Modified + +- `SharepointToolbox/Core/Models/AppSettings.cs` - AppSettings model; DataFolder + Lang with camelCase JSON +- `SharepointToolbox/Infrastructure/Persistence/ProfileRepository.cs` - File I/O; SemaphoreSlim; write-then-replace; camelCase +- `SharepointToolbox/Infrastructure/Persistence/SettingsRepository.cs` - Same pattern as ProfileRepository for settings +- `SharepointToolbox/Services/ProfileService.cs` - CRUD on profiles; validates Name/TenantUrl/ClientId; throws KeyNotFoundException +- `SharepointToolbox/Services/SettingsService.cs` - Get/SetLanguage/SetDataFolder; validates language codes +- `SharepointToolbox.Tests/Services/ProfileServiceTests.cs` - 10 tests: round-trip, missing file, corrupt JSON, concurrency, schema keys +- `SharepointToolbox.Tests/Services/SettingsServiceTests.cs` - 8 tests: defaults, round-trip, JSON keys, tmp file, language/folder persistence + +## Decisions Made + +- Explicit `using System.IO;` required in WPF main project — the WPF temp build project does not include `System.IO` in its implicit usings, unlike the standard non-WPF SDK. All repositories need explicit namespace imports. +- `SettingsService.SetLanguageAsync` validates only "en" and "fr" using a case-insensitive `HashSet`. Other codes throw `ArgumentException` immediately. +- `LoadAsync` on corrupt JSON throws `InvalidDataException` (not silent empty list/default) — this is an explicit safety decision: silently discarding corrupt data could mask accidental overwrites. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] Added explicit System.IO using to WPF project files** +- **Found during:** Task 1 (dotnet test — first GREEN attempt) +- **Issue:** WPF temporary build project does not include `System.IO` in its implicit usings. `File`, `Path`, `Directory`, `IOException`, `InvalidDataException` all unresolved in the main project and test project. +- **Fix:** Added `using System.IO;` at the top of ProfileRepository.cs, SettingsRepository.cs, ProfileServiceTests.cs, and SettingsServiceTests.cs +- **Files modified:** All 4 implementation and test files +- **Verification:** Build succeeded with 0 errors, 18/18 tests pass +- **Committed in:** 769196d and ac3fa5c (inline with respective task commits) + +--- + +**Total deviations:** 1 auto-fixed (Rule 3 — blocking build issue) +**Impact on plan:** One-line fix per file, no logic changes, no scope creep. + +## Issues Encountered + +None beyond the auto-fixed deviation above. + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- ProfileService and SettingsService ready for injection in plan 01-04 (MsalClientFactory may need tenant list from ProfileService) +- SettingsService.SetLanguageAsync ready for TranslationSource in plan 01-05 +- Both services follow the same constructor injection pattern — ready for DI container registration in plan 01-06 or 01-07 +- JSON schema contracts locked: field names are tested and verified camelCase + +--- +*Phase: 01-foundation* +*Completed: 2026-04-02*