Files
Sharepoint-Toolbox/.planning/milestones/v1.0-phases/01-foundation/01-03-SUMMARY.md
Dev 724fdc550d chore: complete v1.0 milestone
Archive 5 phases (36 plans) to milestones/v1.0-phases/.
Archive roadmap, requirements, and audit to milestones/.
Evolve PROJECT.md with shipped state and validated requirements.
Collapse ROADMAP.md to one-line milestone summary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 09:19:03 +02:00

7.2 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
01-foundation 03 persistence
dotnet10
csharp
system-text-json
semaphoreslim
write-then-replace
unit-tests
xunit
01-01 (solution scaffold, test project)
01-02 (TenantProfile model)
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
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)
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
created modified
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
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
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
FOUND-02
FOUND-10
FOUND-12
8min 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<string>. 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