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>
9.0 KiB
9.0 KiB
phase, title, status, created
| phase | title | status | created |
|---|---|---|---|
| 1 | Foundation | ready-for-planning | 2026-04-02 |
Phase 1 Context: Foundation
Decided Areas (from prior research + STATE.md)
These are locked — do not re-litigate during planning or execution.
| Decision | Value |
|---|---|
| Runtime | .NET 10 LTS + WPF |
| MVVM framework | CommunityToolkit.Mvvm 8.4.2 |
| SharePoint library | PnP.Framework 1.18.0 |
| Auth | MSAL.NET 4.83.1 + Extensions.Msal 4.83.3 + Desktop 4.82.1 |
| Token cache | MsalCacheHelper — one IPublicClientApplication per ClientId |
| DI host | Microsoft.Extensions.Hosting 10.x |
| Logging | Serilog 4.3.1 + rolling file sink → %AppData%\SharepointToolbox\logs\ |
| JSON | System.Text.Json (built-in) |
| JSON persistence | Write-then-replace (file.tmp → validate → File.Move) + SemaphoreSlim(1) per file |
| Async pattern | AsyncRelayCommand everywhere — zero async void handlers |
| Trimming | PublishTrimmed=false — accept ~150–200 MB EXE |
| Architecture | 4-layer MVVM: View → ViewModel → Service → Infrastructure |
| Cross-VM messaging | WeakReferenceMessenger for tenant-switched events |
| Session holder | Singleton SessionManager — only class that holds ClientContext objects |
| Localization | .resx resource files (EN default, FR overlay) |
Gray Areas — Defaults Applied (user skipped discussion)
1. Shell Layout
Default: Mirror the existing tool's spatial contract — users are already trained on it.
- Window structure:
MainWindowwith a topToolBar, a centerTabControl(feature tabs), and a bottom docked log panel. - Log panel: Always visible, 150 px tall, not collapsible in Phase 1 (collapsibility is cosmetic — defer to a later phase). Uses a
RichTextBox-equivalent (RichTextBoxXAML control) with color-coded entries. - Tab strip:
TabControlwith oneTabItemper feature area. Phase 1 delivers a shell with placeholder tabs for all features so navigation is wired from day one. - Tabs to stub out: Permissions, Storage, File Search, Duplicates, Templates, Bulk Operations, Folder Structure, Settings — all stubbed with a
"Coming soon"placeholderTextBlockexcept Settings (partially functional in Phase 1 for profile management and language switching). - Status bar:
StatusBarat the very bottom (below the log panel) showing: current tenant display name | operation status text | progress percentage.
2. Tenant Selector Placement
Default: Prominent top-toolbar presence — tenant context is the most critical runtime state.
- Toolbar layout (left to right):
ComboBox(tenant display name list, ~220 px wide) →Button "Connect"→Button "Manage Profiles..."→ separator →Button "Clear Session". - ComboBox: Bound to
MainWindowViewModel.TenantProfilesObservableCollection. Selecting a different item triggers a tenant-switch command (WeakReferenceMessenger broadcast to reset all feature VMs). - "Manage Profiles..." button: Opens a modal
ProfileManagementDialog(separate Window) for CRUD — create, rename, delete profiles. Inline editing in the toolbar would be too cramped. - "Clear Session" button: Clears the MSAL token cache for the currently selected tenant and resets connection state. Lives in the toolbar (not buried in settings) because MSP users need quick access when switching client accounts mid-session.
- Profile fields: Name (display label), Tenant URL, Client ID — matches existing
{ name, tenantUrl, clientId }JSON schema exactly.
3. Progress + Cancel UX
Default: Per-tab pattern — each feature tab owns its progress state. No global progress bar.
- Per-tab layout (bottom of each tab's content area):
ProgressBar(indeterminate or 0–100) +TextBlock(operation description, e.g. "Scanning site 3 of 12…") +Button "Cancel"— shown only when an operation is running (Visibilitybound toIsRunning). CancellationTokenSource: Owned by each ViewModel, recreated per operation. Cancel button calls_cts.Cancel().IProgress<OperationProgress>:OperationProgressis a shared record{ int Current, int Total, string Message }— defined in theCore/layer and used by all feature services. Concrete implementation usesProgress<T>which marshals to the UI thread automatically.- Log panel as secondary channel: Every progress step that produces a meaningful event also writes a timestamped line to the log panel. The per-tab progress bar is the live indicator; the log is the audit trail.
- Status bar:
StatusBarat the bottom updates its operation text from the active tab's progress events via WeakReferenceMessenger — so the user sees progress even if they switch away from the running tab.
4. Error Surface UX
Default: Log panel as primary surface; modal dialog only for blocking errors.
- Non-fatal errors (an operation failed, a SharePoint call returned an error): Written to log panel in red. The per-tab status area shows a brief summary (e.g. "Completed with 2 errors — see log"). No modal.
- Fatal/blocking errors (auth failure, unhandled exception):
MessageBox.Showmodal with the error message and a "Copy to Clipboard" button for diagnostics. Keep it simple — no custom dialog in Phase 1. - No toasts in Phase 1: Toast/notification infrastructure is a cosmetic feature — defer. The log panel is always visible and sufficient.
- Log entry format:
HH:mm:ss [LEVEL] Message— color coded: green = info/success, orange = warning, red = error.LEVELmaps to Serilog severity. - Global exception handler:
Application.DispatcherUnhandledExceptionandTaskScheduler.UnobservedTaskExceptionboth funnel to the log panel + a fatal modal. Neither swallows the exception. - Empty catch block policy: Any
catchblock must do exactly one of: log-and-recover, log-and-rethrow, or log-and-surface. Empty catch = build defect. Enforce via code review on every PR in Phase 1.
JSON Compatibility
Existing file names and schema must be preserved exactly — users have live data in these files.
| File | Schema |
|---|---|
Sharepoint_Export_profiles.json |
{ "profiles": [{ "name": "...", "tenantUrl": "...", "clientId": "..." }] } |
Sharepoint_Settings.json |
{ "dataFolder": "...", "lang": "en" } |
The C# SettingsService must read these files without migration — the field names are the contract.
Localization
- EN strings are the default
.resx—Strings.resx(neutral/EN). FR isStrings.fr.resx. - Key naming: Mirror existing PowerShell key convention (
tab.perms,btn.run.scan,menu.language, etc.) so the EN default content is easily auditable against the existing app. - Dynamic switching:
CultureInfo.CurrentUICultureswap +WeakReferenceMessengerbroadcast triggers all boundLocalizedStringmarkup extensions to re-evaluate. No app restart needed. - FR completeness: FR strings will be stubbed with EN fallback in Phase 1 — FR completeness is a Phase 5 concern.
Infrastructure Patterns (Phase 1 Deliverables)
These are shared helpers that all feature phases reuse. They must be built and tested in Phase 1 before any feature work begins.
SharePointPaginationHelper— static helper that wrapsCamlQuerywithRowLimit ≤ 2,000andListItemCollectionPositionlooping. All list enumeration in the codebase must call this — never rawExecuteQueryon a list.AsyncRelayCommandpattern — a thin base or exampleFeatureViewModelthat demonstrates the canonical async command pattern: createCancellationTokenSource, bindIsRunning, bindIProgress<OperationProgress>, handleOperationCanceledExceptiongracefully.ObservableCollectionthreading rule — results are accumulated inList<T>on a background thread, then assigned asnew ObservableCollection<T>(list)viaDispatcher.InvokeAsync. Never modify anObservableCollectionfromTask.Run.ExecuteQueryRetryAsyncwrapper — wraps PnP Framework's retry logic. All CSOM calls use this; surface retry events as log + progress messages ("Throttled — retrying in 30s…").ClientContextdisposal — alwaysawait using. Unit tests verifyDispose()is called on cancellation.
Deferred Ideas (out of scope for Phase 1)
- Log panel collapsibility (cosmetic, Phase 3+)
- Dark/light theme toggle (cosmetic, post-v1)
- Toast/notification system (Phase 3+)
- FR locale completeness (Phase 5)
- User access export, storage charts, simplified permissions view (v1.x features, Phase 5)
code_context
| Asset | Path | Notes |
|---|---|---|
| Existing profile JSON schema | Sharepoint_ToolBox.ps1:68–72 |
Save-Profiles shows exact field names |
| Existing settings JSON schema | Sharepoint_ToolBox.ps1:147–152 |
Save-Settings shows dataFolder + lang |
| Existing localization keys (EN) | Sharepoint_ToolBox.ps1:2795–2870 (approx) |
Full EN key set for .resx migration |
| Existing tab names | Sharepoint_ToolBox.ps1:3824 |
9 tabs: Perms, Storage, Templates, Search, Dupes, Transfer, Bulk, Struct, Versions |
| Log panel pattern | Sharepoint_ToolBox.ps1:6–17 |
Color + timestamp format to mirror |