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>
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
---
|
||||
gsd_state_version: 1.0
|
||||
milestone: v1.0
|
||||
milestone_name: milestone
|
||||
milestone_name: MVP
|
||||
status: completed
|
||||
stopped_at: Completed 05-03-PLAN.md — Phase 5 and project fully complete
|
||||
last_updated: "2026-04-07T06:51:37.208Z"
|
||||
last_activity: 2026-04-03 — Plan 05-03 complete — 134 tests pass, EXE verified, French locale approved by human
|
||||
stopped_at: Milestone v1.0 archived — all 5 phases shipped
|
||||
last_updated: "2026-04-07T09:00:00.000Z"
|
||||
last_activity: 2026-04-07 — v1.0 milestone completed and archived
|
||||
progress:
|
||||
total_phases: 5
|
||||
completed_phases: 5
|
||||
@@ -18,187 +18,33 @@ progress:
|
||||
|
||||
## Project Reference
|
||||
|
||||
See: .planning/PROJECT.md (updated 2026-04-02)
|
||||
See: .planning/PROJECT.md (updated 2026-04-07)
|
||||
|
||||
**Core value:** Administrators can audit and manage SharePoint/Teams permissions and storage across multiple client tenants from a single, reliable desktop application.
|
||||
**Current focus:** Phase 4 — Bulk Operations and Provisioning (not yet planned)
|
||||
**Current focus:** v1.0 shipped — planning next milestone
|
||||
|
||||
## Current Position
|
||||
|
||||
Phase: 5 of 5 (Distribution and Hardening) — COMPLETE
|
||||
Plan: 3 of 3 in phase 05 — Integration gate passed, human smoke test approved
|
||||
Status: ALL PHASES COMPLETE — application ready for distribution
|
||||
Last activity: 2026-04-03 — Plan 05-03 complete — 134 tests pass, EXE verified, French locale approved by human
|
||||
|
||||
Progress: [██████████] 100%
|
||||
|
||||
## Phase 3 Wave Structure
|
||||
|
||||
| Wave | Plans | Autonomous | Description |
|
||||
|------|-------|------------|-------------|
|
||||
| 0 | 03-01 | yes | Models, interfaces, export stubs, test scaffolds |
|
||||
| 1 | 03-02 | yes | StorageService implementation |
|
||||
| 2 | 03-03, 03-04, 03-06 | yes | Storage exports + Search/Duplicates services + Localization (parallel) |
|
||||
| 3 | 03-05, 03-07 | yes | Search/Duplicate exports + StorageViewModel/View (parallel) |
|
||||
| 4 | 03-08 | no (checkpoint) | SearchViewModel + DuplicatesViewModel + Views + visual checkpoint |
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
**Velocity:**
|
||||
- Total plans completed: 0
|
||||
- Average duration: —
|
||||
- Total execution time: 0 hours
|
||||
|
||||
**By Phase:**
|
||||
|
||||
| Phase | Plans | Total | Avg/Plan |
|
||||
|-------|-------|-------|----------|
|
||||
| - | - | - | - |
|
||||
|
||||
**Recent Trend:**
|
||||
- Last 5 plans: —
|
||||
- Trend: —
|
||||
|
||||
*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 |
|
||||
| Phase 01-foundation P05 | 4min | 2 tasks | 8 files |
|
||||
| Phase 01-foundation P04 | 4 | 2 tasks | 4 files |
|
||||
| Phase 01-foundation P06 | 5 | 2 tasks | 12 files |
|
||||
| Phase 01-foundation P07 | 3 | 2 tasks | 8 files |
|
||||
| Phase 01-foundation P08 | 5 | 1 tasks | 1 files |
|
||||
| Phase 01-foundation P08 | 15 | 2 tasks | 3 files |
|
||||
| Phase 02-permissions P05 | 1min | 1 tasks | 3 files |
|
||||
| Phase 02-permissions P03 | 1min | 1 tasks | 5 files |
|
||||
| Phase 02-permissions P01 | 5min | 2 tasks | 9 files |
|
||||
| Phase 02-permissions P02 | 7min | 2 tasks | 4 files |
|
||||
| Phase 02-permissions P04 | 1min | 2 tasks | 2 files |
|
||||
| Phase 02-permissions P06 | 4min | 2 tasks | 6 files |
|
||||
| Phase 02-permissions P07 | 30min | 2 tasks | 6 files |
|
||||
| Phase 03-storage P01 | 10min | 2 tasks | 22 files |
|
||||
| Phase 03-storage P03 | 2min | 2 tasks | 2 files |
|
||||
| Phase 03-storage P06 | 5min | 1 tasks | 3 files |
|
||||
| Phase 03-storage P04 | 2min | 2 tasks | 2 files |
|
||||
| Phase 03-storage P07 | 4min | 2 tasks | 10 files |
|
||||
| Phase 03-storage P05 | 4min | 2 tasks | 3 files |
|
||||
| Phase 03 P08 | 4min | 3 tasks | 9 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P01 | 7min | 2 tasks | 27 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P05 | 6min | 2 tasks | 3 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P03 | 7min | 2 tasks | 2 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P02 | 25 | 2 tasks | 4 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P04 | 7min | 2 tasks | 3 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P06 | 10min | 2 tasks | 4 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P07 | 15 | 2 tasks | 11 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P08 | 20min | 2 tasks | 6 files |
|
||||
| Phase 04-bulk-operations-and-provisioning P10 | 25min | 2 tasks | 7 files |
|
||||
| Phase 05-distribution-and-hardening P02 | 3min | 2 tasks | 2 files |
|
||||
| Phase 05-distribution-and-hardening P01 | 2min | 2 tasks | 5 files |
|
||||
| Phase 05-distribution-and-hardening P03 | 15min | 2 tasks | 0 files |
|
||||
Milestone: v1.0 MVP — SHIPPED 2026-04-07
|
||||
Status: All 5 phases complete, archived to .planning/milestones/
|
||||
Next: `/gsd:new-milestone` to start v1.1
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
### Decisions
|
||||
|
||||
Decisions are logged in PROJECT.md Key Decisions table.
|
||||
Recent decisions affecting current work:
|
||||
|
||||
- Foundation: Use PnP.Framework 1.18.0 (not PnP.Core SDK) — PnP Provisioning Engine lives only in PnP.Framework
|
||||
- Foundation: Use MsalCacheHelper for per-tenant token cache serialization — scope IPublicClientApplication per ClientId
|
||||
- Foundation: Never set PublishTrimmed=true — PnP.Framework and MSAL use reflection; accept ~150-200 MB EXE
|
||||
- Foundation: Establish AsyncRelayCommand + IProgress<T> + CancellationToken patterns before any feature work — retrofitting is the most expensive WPF refactor
|
||||
- [Phase 01-foundation]: Upgraded MSAL from 4.83.1 to 4.83.3 — Extensions.Msal 4.83.3 requires MSAL >= 4.83.3; minor patch with no behavioral difference
|
||||
- [Phase 01-foundation]: Test project targets net10.0-windows with UseWPF=true — required to reference WPF main project; net10.0 is framework-incompatible
|
||||
- [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
|
||||
- [Phase 01-foundation]: Strings.Designer.cs maintained manually — ResXFileCodeGenerator is VS-only, not run by dotnet build; only ResourceManager accessor needed
|
||||
- [Phase 01-foundation]: EmbeddedResource uses Update not Include in SDK-style project — SDK auto-includes all .resx; Include causes NETSDK1022 duplicate error
|
||||
- [Phase 01-foundation]: MsalClientFactory stores MsalCacheHelper per clientId and exposes GetCacheHelper() — PnP creates its own internal PCA so tokenCacheCallback is the bridge for shared persistent cache
|
||||
- [Phase 01-foundation]: SessionManager is the single holder of ClientContext instances — callers must not store returned contexts
|
||||
- [Phase 01-foundation]: CacheDirectory is a constructor parameter (no-arg defaults to AppData) — enables test isolation without real filesystem writes
|
||||
- [Phase 01-foundation]: Interactive login test marked Skip in unit suite — browser/WAM MSAL flow cannot run in automated CI
|
||||
- [Phase 01-foundation]: ObservableRecipient lambda receivers need explicit cast to FeatureViewModelBase for virtual dispatch
|
||||
- [Phase 01-foundation]: FeatureViewModelBase declared as abstract partial class — CommunityToolkit.Mvvm source generator requires partial keyword
|
||||
- [Phase 01-foundation]: OpenFolderDialog (Microsoft.Win32) used in WPF instead of FolderBrowserDialog (System.Windows.Forms)
|
||||
- [Phase 01-foundation]: LogPanel exposed via GetLogPanel() method — x:Name generates field in XAML partial class, property with same name causes CS0102
|
||||
- [Phase 01-foundation]: ProfileManagementViewModel dialog factory pattern — ViewModel exposes Func<Window>? OpenProfileManagementDialog set by View layer; avoids Window/DI coupling in ViewModel
|
||||
- [Phase 01-foundation]: IServiceProvider injected into MainWindow constructor — resolves DI-registered ProfileManagementDialog and SettingsView at runtime
|
||||
- [Phase 01-foundation]: ProfileManagementDialog and SettingsView registered as Transient — fresh instance with fresh ViewModel per dialog open or tab init
|
||||
- [Phase 01-foundation]: Solution file is .slnx (not .sln) — dotnet build/test commands must use SharepointToolbox.slnx
|
||||
- [Phase 01-foundation]: 45 tests total: 44 pass, 1 skip (interactive MSAL GetOrCreateContextAsync_CreatesContext — browser/WAM flow excluded from automated suite)
|
||||
- [Phase 02-permissions]: DeriveAdminUrl is internal static — enables direct unit testing of admin URL regex without live tenant
|
||||
- [Phase 02-permissions]: InternalsVisibleTo added to AssemblyInfo.cs — required for test project to access internal DeriveAdminUrl; plan omitted this assembly attribute
|
||||
- [Phase 02-permissions]: Export service stubs created in Plan 02-01 so test project compiles before Plan 03 implementation
|
||||
- [Phase 02-permissions]: Principal.Email removed from CSOM load expression — Email only exists on User subtype, not Principal base class
|
||||
- [Phase 02-permissions]: Folder is not a SecurableObject in CSOM — ListItem used for permission extraction — Required by CSOM type system; Folder inherits from ClientObject not SecurableObject
|
||||
- [Phase 02-permissions]: Principal.Email excluded from CSOM Include — email not needed for PermissionEntry — Principal base type has no Email property; only User subtype does; avoids CS1061
|
||||
- [Phase 02-permissions]: CsvExportService uses UTF-8 with BOM for Excel compatibility; HtmlExportService uses UTF-8 without BOM
|
||||
- [Phase 02-permissions]: ISessionManager interface extracted from concrete SessionManager — required for Moq-based unit testing of PermissionsViewModel
|
||||
- [Phase 02-permissions]: PermissionsView code-behind wires Func<TenantProfile, SitePickerDialog> factory via DI — avoids Window coupling in ViewModel, keeps ViewModel testable
|
||||
- [Phase 02-permissions]: ISessionManager -> SessionManager DI registration was missing from App.xaml.cs — added in plan 02-07 (auto-detected Rule 3 blocker)
|
||||
- [Phase 02-permissions]: MainWindow.xaml uses x:Name on Permissions TabItem; MainWindow.xaml.cs sets Content at runtime from DI — same pattern as SettingsView
|
||||
- [Phase 03-storage]: Storage display uses flat DataGrid with IndentLevel -> Margin IValueConverter (not WPF TreeView) — better UI virtualization for large sites
|
||||
- [Phase 03-storage]: StorageNode.VersionSizeBytes is a derived property (TotalSizeBytes - FileStreamSizeBytes, Math.Max 0) — not stored separately
|
||||
- [Phase 03-storage]: SearchService uses KeywordQuery + SearchExecutor (Microsoft.SharePoint.Client.Search.Query) — transitive dep of PnP.Framework; no new NuGet package
|
||||
- [Phase 03-storage]: Search pagination: StartRow += 500, hard cap StartRow <= 50,000 (SharePoint Search boundary) = 50,000 max results
|
||||
- [Phase 03-storage]: DuplicatesService uses CAML FSObjType=1 (not FileSystemObjectType) for folder queries — wrong name returns zero results silently
|
||||
- [Phase 03-storage]: Duplicate detection uses composite key grouping (name+size+dates), no content hashing — matches PS reference and DUPL-01/02/03 requirements exactly
|
||||
- [Phase 03-storage]: Phase 3 export services are separate classes from Phase 2 (StorageCsvExportService, SearchCsvExportService, etc.) — different schemas
|
||||
- [Phase 03-storage]: StorageNode.VersionSizeBytes is a derived property (Math.Max(0, TotalSizeBytes - FileStreamSizeBytes)) — not stored separately
|
||||
- [Phase 03-storage]: MakeKey composite key logic tested inline in DuplicatesServiceTests before Plan 03-04 creates the real class — avoids skipping all duplicate logic tests
|
||||
- [Phase 03-storage]: Export service stubs return string.Empty until implemented — compile-only skeletons for Plans 03-03 and 03-05
|
||||
- [Phase 03-storage 03-02]: StorageService.LastModified uses StorageMetrics.LastModified with fallback to Folder.TimeLastModified — StorageMetrics.LastModified may be DateTime.MinValue for empty libraries
|
||||
- [Phase 03-storage 03-02]: System folder filter uses Forms/ and _-prefix heuristic — matches SharePoint standard hidden folder naming convention
|
||||
- [Phase 03-storage]: Explicit System.IO using required in StorageCsvExportService and StorageHtmlExportService — WPF project does not include System.IO in implicit usings (established project pattern)
|
||||
- [Phase 03-storage 03-04]: SearchService uses SelectProperties.Add per-item loop — StringCollection has no AddRange(string[]) overload in this SDK version
|
||||
- [Phase 03-storage 03-04]: DuplicatesService.MakeKey internal static method matches inline test helper in DuplicatesServiceTests exactly — deliberate design to ensure test parity
|
||||
- [Phase 03-storage 03-04]: DuplicatesService file mode re-implements pagination inline — avoids coupling between services with different result models (DuplicateItem vs SearchResult)
|
||||
- [Phase 03-storage]: ClientContext.Url is read-only in CSOM — site URL override done via new TenantProfile with site URL for GetOrCreateContextAsync
|
||||
- [Phase 03-storage]: IndentConverter/BytesConverter/InverseBoolConverter registered in App.xaml Application.Resources — accessible to all views without per-UserControl declaration
|
||||
- [Phase 03-storage]: SearchCsvExportService uses UTF-8 BOM for Excel compatibility — consistent with Phase 2 CsvExportService pattern
|
||||
- [Phase 03-storage]: DuplicatesHtmlExportService always uses badge-dup (red) for all groups — ok/diff distinction removed from final DUPL-03 spec
|
||||
- [Phase 03]: SearchViewModel and DuplicatesViewModel use TenantProfile site URL override pattern — ctx.Url is read-only in CSOM (established pattern from StorageViewModel)
|
||||
- [Phase 03]: DuplicateRow flat DTO wraps DuplicateItem with GroupName and GroupSize for DataGrid display
|
||||
- [Phase 04-bulk-operations-and-provisioning]: ITemplateService uses ModelSiteTemplate alias — SiteTemplate is ambiguous between SharepointToolbox.Core.Models and Microsoft.SharePoint.Client; resolved with using alias
|
||||
- [Phase 04-bulk-operations-and-provisioning]: ICsvValidationService and BulkResultCsvExportService require explicit System.IO using — WPF project does not include System.IO in implicit usings (established project pattern)
|
||||
- [Phase 04-bulk-operations-and-provisioning]: BulkSiteService uses Core.Helpers.ExecuteQueryRetryHelper not Infrastructure.Auth — plan had wrong using; correct namespace is Core.Helpers (established pattern from Phase 2/3 services)
|
||||
- [Phase 04-bulk-operations-and-provisioning]: Design-time MSBuild compile used for build verification — dotnet build WinFX BAML step fails in bash shell; C# compilation verified via dotnet msbuild -t:Compile -p:DesignTimeBuild=true with 0 errors; DLL-based test run confirms 4 pass / 3 skip
|
||||
- [Phase 04-bulk-operations-and-provisioning]: CsvValidationService uses DetectDelimiter=true — handles both comma and semicolon CSV files without format-specific code paths
|
||||
- [Phase 04-bulk-operations-and-provisioning]: TemplateRepository uses same atomic write pattern as SettingsRepository (tmp + File.Move + round-trip JSON validation)
|
||||
- [Phase 04-bulk-operations-and-provisioning 04-04]: GraphClientFactory uses GetOrCreateAsync (async) — MsalClientFactory only exposes async method with SemaphoreSlim locking; plan had incorrect sync reference GetOrCreateClient
|
||||
- [Phase 04-bulk-operations-and-provisioning 04-04]: AuthGraphClientFactory alias resolves CS0104 — Microsoft.Graph.GraphClientFactory conflicts with SharepointToolbox.Infrastructure.Auth.GraphClientFactory when both namespaces imported
|
||||
- [Phase 04-bulk-operations-and-provisioning 04-04]: Microsoft.SharePoint.Client.Group? fully qualified in AddToClassicGroupAsync — Microsoft.Graph.Models.Group also in scope in BulkMemberService; explicit namespace resolves ambiguity
|
||||
- [Phase 04-bulk-operations-and-provisioning]: TemplateService uses ModelSiteTemplate alias — consistent with ITemplateService; CSOM SiteTemplate and Core.Models.SiteTemplate are both in scope
|
||||
- [Phase 04-bulk-operations-and-provisioning]: FolderStructureService.BuildUniquePaths sorts by slash count for parent-first ordering — ensures intermediate folders exist before children when using Folders.Add
|
||||
- [Phase 04-bulk-operations-and-provisioning]: TemplateService.ApplyTemplateAsync creates new ClientContext for new site URL — adminCtx.Url points to admin site, new site needs separate context
|
||||
- [Phase 04-bulk-operations-and-provisioning]: FolderBrowserDialog uses Core.Helpers.ExecuteQueryRetryHelper (not Infrastructure.Auth) — consistent with established project namespace pattern
|
||||
- [Phase 04-bulk-operations-and-provisioning]: Example CSV files placed in Resources/ and registered as EmbeddedResource — accessible via Assembly.GetManifestResourceStream without file system dependency
|
||||
- [Phase 04-bulk-operations-and-provisioning]: SitePickerDialog.SelectedUrls.FirstOrDefault() used for single-site transfer selection — avoids new dialog variant while reusing existing dialog
|
||||
- [Phase 04-bulk-operations-and-provisioning]: EnumBoolConverter added for RadioButton-to-enum binding (TransferMode); ConverterParameter=EnumValueName pattern established for future ViewModels
|
||||
- [Phase 04-bulk-operations-and-provisioning 04-09]: BulkMembersViewModel passes _currentProfile.ClientId to AddMembersAsync — IBulkMemberService interface requires clientId for Graph API authentication; plan code omitted this parameter
|
||||
- [Phase 04-bulk-operations-and-provisioning 04-09]: Duplicate standalone converter files (EnumBoolConverter.cs etc.) removed — already defined in IndentConverter.cs which is the established project pattern for all converters
|
||||
- [Phase 04-bulk-operations-and-provisioning]: TemplatesView uses RenameInputDialog (custom WPF Window) instead of Microsoft.VisualBasic.Interaction.InputBox — avoids additional framework dependency
|
||||
- [Phase 04-bulk-operations-and-provisioning]: All value converters (EnumBoolConverter, StringToVisibilityConverter, ListToStringConverter) added to IndentConverter.cs — consistent co-location pattern
|
||||
- [Phase 05-distribution-and-hardening]: PublishSingleFile PropertyGroup is conditional on '' == 'true' — regular dotnet build and dotnet test are unaffected
|
||||
- [Phase 05-distribution-and-hardening]: IncludeNativeLibrariesForSelfExtract=true required — PnP.Framework has native binaries that must bundle into the EXE
|
||||
- [Phase 05-distribution-and-hardening]: IsThrottleException only checks top-level Message (not InnerException) — documented via nested-throttle test asserting false
|
||||
- [Phase 05-distribution-and-hardening]: FR diacritics already present in Strings.fr.resx — FrStrings_ContainExpectedDiacritics test passes immediately (no diacritic repair needed for those 5 keys)
|
||||
|
||||
### Pending Todos
|
||||
|
||||
None yet.
|
||||
None.
|
||||
|
||||
### Blockers/Concerns
|
||||
|
||||
- Phase 4 planning: PnP Provisioning Engine behavior for Teams-connected modern sites — edge cases need validation spike before planning
|
||||
- Phase 5: User access export (v2 requirement UACC-01/02) depends on Phase 2 PermissionsService — confirm scope before Phase 5 planning
|
||||
None — v1.0 is shipped.
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-04-03T15:00:00.000Z
|
||||
Stopped at: Completed 05-03-PLAN.md — Phase 5 and project fully complete
|
||||
Last session: 2026-04-07T09:00:00.000Z
|
||||
Stopped at: Milestone v1.0 archived
|
||||
Resume file: None
|
||||
|
||||
Reference in New Issue
Block a user