Files
Sharepoint-Toolbox/.planning/STATE.md
Dev fd442f3b4c
Some checks failed
Release SharePoint Toolbox v2 / release (push) Failing after 14s
chore: archive v1.1 Enhanced Reports milestone
v1.1 shipped with 4 phases (25 plans), 10/10 requirements complete:
- Global site selection (toolbar picker, all tabs consume)
- User access audit (Graph people-picker, direct/group/inherited)
- Simplified permissions (plain-language labels, risk levels, detail toggle)
- Storage visualization (LiveCharts2 pie/donut + bar charts)

Post-phase polish: centralized site selection (removed per-tab pickers),
claims prefix stripping, StorageMetrics backfill, chart tooltip fix,
summary stats in app + HTML exports.

205 tests passing, 10,484 LOC.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:21:02 +02:00

9.0 KiB

gsd_state_version, milestone, milestone_name, status, stopped_at, last_updated, last_activity, progress
gsd_state_version milestone milestone_name status stopped_at last_updated last_activity progress
1.0 v1.1 v1.1 Enhanced Reports shipped Milestone archived 2026-04-08T00:00:00Z 2026-04-08 — v1.1 milestone archived and tagged
total_phases completed_phases total_plans completed_plans
4 4 25 25

Project State

Project Reference

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: v1.1 Enhanced Reports — global site selection, user access audit, simplified permissions, storage visualization

Current Position

Phase: 9 — Storage Visualization Plan: 4 of 4 Status: Plan 09-04 complete — StorageViewModel chart unit tests Last activity: 2026-04-07 — Completed 09-04 (StorageViewModel chart unit tests)

v1.1 Progress: [██████████] 100%
Phase 6 [x] → Phase 7 [x] → Phase 8 [x] → Phase 9 [x]

Performance Metrics

Metric v1.0 v1.1 (running)
Phases 5 4 planned
Plans 36 TBD
Commits 164 0
Tests 134 pass / 22 skip
Phase 06-global-site-selection P02 8 1 tasks
Phase 06-global-site-selection P01 2 2 tasks
Phase 06-global-site-selection P03 2 3 tasks
Phase 06-global-site-selection P04 2 3 tasks
Phase 06-global-site-selection P05 2 1 tasks
Phase 07-user-access-audit P01 5 2 tasks
Phase 07-user-access-audit P03 2 1 tasks
Phase 07-user-access-audit P02 1 1 tasks
Phase 07-user-access-audit P06 2 2 tasks
Phase 07-user-access-audit P04 2 1 tasks
Phase 07-user-access-audit P05 4 2 tasks
Phase 07-user-access-audit P07 8 3 tasks
Phase 07-user-access-audit P08 2 2 tasks
Phase 07-user-access-audit P09 6 1 tasks
Phase 07-user-access-audit P10 5 1 tasks
Phase 08 P02 84 1 tasks
Phase 08 P03 77 1 tasks
Phase 08 P04 2 2 tasks
Phase 08 P05 2 2 tasks
Phase 08 P06 2 2 tasks
Phase 09 P01 1 2 tasks
Phase 09 P02 1 1 tasks
Phase 09 P03 573 2 tasks
Phase 09 P04 146 1 tasks

Accumulated Context

Decisions

Decisions are logged in PROJECT.md Key Decisions table.

v1.1 architectural notes:

  • Global site selection (Phase 6) changes the toolbar; all tabs must bind to a shared GlobalSiteSelectionViewModel or equivalent. Use WeakReferenceMessenger for cross-tab site-changed notifications, consistent with v1.0 messenger usage.
  • Per-tab override (SITE-02) means each FeatureViewModelBase subclass stores a nullable local site override; null means "use global".
  • Storage Visualization (Phase 9) requires a WPF charting NuGet (LiveCharts2 recommended — actively maintained, WPF-native, self-contained friendly). Wire chart data binding to the existing storage scan result model.
  • Self-contained EXE constraint: charting library must not require runtime DLLs outside the publish output.
  • [Phase 06-02]: MainWindowViewModel uses Func? factory for SitePickerDialog and broadcasts GlobalSitesChangedMessage via WeakReferenceMessenger on collection change
  • [Phase 06-01]: GlobalSitesChangedMessage uses IReadOnlyList (snapshot, not ObservableCollection) so receivers cannot mutate sender state
  • [Phase 06-01]: FeatureViewModelBase.OnGlobalSitesReceived (private) updates GlobalSites then calls OnGlobalSitesChanged (protected virtual) — separates storage from derived class hooks
  • [Phase 06-03]: Added using SharepointToolbox.Core.Models to MainWindow.xaml.cs for TenantProfile in SitePickerDialog factory lambda
  • [Phase 06-03]: toolbar.selectSites.tooltipDisabled added to resources but not wired in XAML — WPF Button disabled tooltip requires style trigger (deferred)
  • [Phase 06-global-site-selection]: PermissionsViewModel uses _hasLocalSiteOverride guard for SelectedSites; site picker sets flag, tenant switch resets it
  • [Phase 06-global-site-selection]: Single-site VMs use partial void OnSiteUrlChanged to detect local typing; clearing field reverts to global
  • [Phase 06-global-site-selection]: BulkMembersViewModel confirmed excluded: no SiteUrl field, CSV-driven per-row site URLs
  • [Phase 06-global-site-selection]: Test 8 asserts override-reset via next global sites message (not SiteUrl='' — OnSiteUrlChanged re-applies global immediately when cleared)
  • [Phase 06-global-site-selection]: Used reflection to set _hasLocalSiteOverride in PermissionsViewModel test — avoids needing a real SitePickerDialog
  • [Phase 07-01]: UserAccessEntry is fully denormalized (one row = one user + one object + one permission) for direct DataGrid binding
  • [Phase 07-01]: IsHighPrivilege and IsExternalUser pre-computed at scan time; GraphUserResult co-located with IGraphUserSearchService interface
  • [Phase 07-03]: Minimum 2-character query guard prevents overly broad Graph API requests
  • [Phase 07-03]: OData single-quote escaping (replace apostrophe with two apostrophes) prevents injection in startsWith filter
  • [Phase 07-03]: ConsistencyLevel=eventual and Count=true both required for startsWith on Graph directory objects
  • [Phase 07-user-access-audit]: TenantProfile.ClientId empty in service — session pre-authenticated at ViewModel level; SessionManager returns cached context by URL key
  • [Phase 07-user-access-audit]: Bidirectional contains matching for user login — handles both plain email and full SharePoint claim formats
  • [Phase 07-user-access-audit]: UserAccessCsvExportService has two write modes: WriteAsync (per-user files to directory) and WriteSingleFileAsync (combined for SaveFileDialog)
  • [Phase 07-user-access-audit]: HTML sortTable() scoped per group so sorting in by-user view keeps each user's rows together
  • [Phase 07-04]: CollectionViewSource bound at construction; ApplyGrouping() swaps PropertyGroupDescription between UserLogin/SiteUrl on IsGroupByUser toggle
  • [Phase 07-04]: ExportCsvAsync uses WriteSingleFileAsync (combined file) not WriteAsync (per-user directory) to match SaveFileDialog single-path UX
  • [Phase 07-05]: Autocomplete ListBox visibility managed via code-behind CollectionChanged — WPF DataTrigger cannot compare to non-zero Count without converter
  • [Phase 07-05]: Simple ListBox autocomplete (not Popup) following plan's recommended simpler alternative — avoids Popup placement issues
  • [Phase 07-user-access-audit]: Dialog factory wiring in MainWindow.xaml.cs by casting auditView.DataContext to UserAccessAuditViewModel — matches PermissionsView pattern
  • [Phase 07-user-access-audit]: UserAccessAuditView created inline (Rule 3) when 07-05 found missing — follows 07-05 spec with two-panel layout
  • [Phase 07-user-access-audit]: Used internal TestRunOperationAsync for ViewModel tests; Application.Current null in tests lets else branch run synchronously
  • [Phase 07-user-access-audit]: WeakReferenceMessenger.Default.Reset() in test constructor prevents cross-test contamination from message registrations
  • [Phase 07-09]: Guest badge (orange pill) and warning icon (⚠) use DataTrigger-driven Visibility on DataGridTemplateColumn cells — collapsed by default, visible only when IsExternalUser/IsHighPrivilege=True
  • [Phase 07-10]: Extended CreateViewModel to 3-tuple (vm, auditMock, graphMock) so debounce test can verify SearchUsersAsync calls
  • [Phase 08]: ActiveItemsSource returns Results or SimplifiedResults based on IsSimplifiedMode -- View binds to single property
  • [Phase 08]: InvertBoolConverter in Core/Converters namespace for reuse; summary cards use WrapPanel; row color triggers only match SimplifiedPermissionEntry
  • [Phase 08]: FR translations use XML entities for accented chars matching existing resx convention
  • [Phase 09-01]: LiveChartsCore.SkiaSharpView.WPF 2.0.0-rc5.4 added as charting library; SkiaSharp backend for self-contained EXE compatibility
  • [Phase 09-01]: FileTypeMetric record uses Extension (with dot), TotalSizeBytes (long), FileCount (int), DisplayLabel (computed) matching existing model patterns
  • [Phase 09-01]: CollectFileTypeMetricsAsync omits StorageScanOptions since file-type scan covers all non-hidden libraries without folder depth filtering
  • [Phase 09-02]: Added System.IO using explicitly -- WPF project implicit usings do not include System.IO for Path.GetExtension
  • [Phase 09]: Used wrapper Grid elements with MultiDataTrigger for LiveCharts2 chart visibility -- more reliable than styling third-party controls directly

Pending Todos

  1. Add global multi-site selection option (ui) — todos/pending/2026-04-07-add-global-multi-site-selection-option.mdaddressed by Phase 6

Blockers/Concerns

None.

Session Continuity

Last session: 2026-04-07T13:40:30Z Stopped at: Completed 09-04-PLAN.md Resume file: None