21 KiB
phase, verified, status, score, human_verification
| phase | verified | status | score | human_verification | |||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 04-bulk-operations-and-provisioning | 2026-04-03T00:00:00Z | human_needed | 12/12 must-haves verified |
|
Phase 4: Bulk Operations and Provisioning — Verification Report
Phase Goal: Users can execute bulk write operations (member additions, site creation, file transfer) with per-item error reporting and cancellation, capture site structures as reusable templates, apply templates to create new sites, and provision folder structures from CSV — all without silent partial failures.
Verified: 2026-04-03 Status: human_needed — All automated checks passed; 7 items require live UI or connected-tenant verification. Re-verification: No — initial verification.
Goal Achievement
Observable Truths
| # | Truth | Status | Evidence |
|---|---|---|---|
| 1 | Bulk write operations continue on error and report per-item results | VERIFIED | BulkOperationRunner.RunAsync catches per-item exceptions, wraps in BulkItemResult<T>.Failed, continues loop. 5 unit tests pass including RunAsync_SomeItemsFail_ContinuesAndReportsPerItem. |
| 2 | Cancellation propagates immediately and stops processing | VERIFIED | OperationCanceledException is re-thrown from BulkOperationRunner.RunAsync. Tests RunAsync_Cancelled_ThrowsOperationCanceled and RunAsync_CancelledMidOperation_StopsProcessing pass. Cancel button wired in all Views via CancelCommand. |
| 3 | File transfer (copy and move) works with per-file error reporting and conflict policies | VERIFIED | FileTransferService uses MoveCopyUtil.CopyFileByPath / MoveFileByPath with ResourcePath.FromDecodedUrl, delegates to BulkOperationRunner.RunAsync. All three conflict policies (Skip/Overwrite/Rename) implemented via MoveCopyOptions. |
| 4 | Bulk member addition uses Graph API for M365 groups with CSOM fallback | VERIFIED | BulkMemberService.AddMembersAsync delegates to BulkOperationRunner.RunAsync. Graph path uses GraphClientFactory+MsalTokenProvider. CSOM path uses EnsureUser + SiteGroups. clientId passed explicitly from ViewModel. |
| 5 | Bulk site creation creates Team and Communication sites with per-site error reporting | VERIFIED | BulkSiteService uses TeamSiteCollectionCreationInformation and CommunicationSiteCollectionCreationInformation via PnP Framework CreateSiteAsync. Delegated to BulkOperationRunner.RunAsync. |
| 6 | Site structures are captured as reusable templates and persisted locally | VERIFIED | TemplateService.CaptureTemplateAsync reads libraries (filtering hidden+system lists), folders (recursive), permission groups, logo, settings via CSOM. TemplateRepository.SaveAsync persists JSON with atomic tmp+Move write. 6 TemplateRepository tests pass. |
| 7 | Templates can be applied to create new sites with the captured structure | VERIFIED | TemplateService.ApplyTemplateAsync creates Team or Communication site via PnP Framework, then recreates libraries, folders (recursive), permission groups via CSOM. Key link to CaptureTemplateAsync / ApplyTemplateAsync in TemplatesViewModel confirmed. |
| 8 | Folder structures can be provisioned from CSV with parent-first ordering | VERIFIED | FolderStructureService.BuildUniquePaths sorts paths by depth. CreateFoldersAsync uses BulkOperationRunner.RunAsync. Tests BuildUniquePaths_FromExampleCsv_ReturnsParentFirst and BuildUniquePaths_DuplicateRows_Deduplicated pass. |
| 9 | CSV validation reports per-row errors before execution | VERIFIED | CsvValidationService uses CsvHelper with DetectDelimiter=true, BOM detection, per-row validation. 9 unit tests pass. DataGrid binds to CsvValidationRow<T>.IsValid and Errors columns. |
| 10 | Failed items can be exported as CSV after partial failures | VERIFIED | BulkResultCsvExportService.BuildFailedItemsCsv writes failed-only rows with Error+Timestamp columns. ExportFailedCommand wired in all 4 bulk operation ViewModels. 2 unit tests pass. |
| 11 | Retry Failed button re-runs only the failed items | VERIFIED | RetryFailedCommand in BulkMembersViewModel and BulkSitesViewModel populates _failedRowsForRetry from _lastResult.FailedItems and re-runs. Button bound in XAML for both tabs. |
| 12 | All 5 new tabs are registered in DI and wired to MainWindow | VERIFIED | All 5 services+ViewModels+Views registered in App.xaml.cs (lines 124-152). All 5 TabItems declared in MainWindow.xaml with named x:Name. Content set from DI in MainWindow.xaml.cs (lines 36-40). |
Score: 12/12 truths verified
Required Artifacts
| Artifact | Expected | Status | Details |
|---|---|---|---|
SharepointToolbox/Services/BulkOperationRunner.cs |
Shared bulk helper with continue-on-error | VERIFIED | RunAsync<TItem> with OperationCanceledException re-throw and per-item catch |
SharepointToolbox/Core/Models/BulkOperationResult.cs |
Per-item result tracking | VERIFIED | BulkItemResult<T> with Success/Failed factories; BulkOperationSummary<T> with HasFailures, FailedItems |
SharepointToolbox/Core/Models/SiteTemplate.cs |
Template JSON model | VERIFIED | SiteTemplate, TemplateSettings, TemplateLogo classes present |
SharepointToolbox/Services/CsvValidationService.cs |
CSV parsing + validation | VERIFIED | CsvHelper with DetectDelimiter, BOM, per-row member/site/folder validation |
SharepointToolbox/Infrastructure/Persistence/TemplateRepository.cs |
JSON persistence for templates | VERIFIED | SemaphoreSlim, atomic tmp+Move write, JsonSerializer, full CRUD |
SharepointToolbox/Services/FileTransferService.cs |
CSOM file transfer | VERIFIED | MoveCopyUtil.CopyFileByPath/MoveFileByPath, ResourcePath.FromDecodedUrl, 3 conflict policies |
SharepointToolbox/Services/BulkMemberService.cs |
Graph + CSOM member addition | VERIFIED | Graph SDK path + CSOM fallback, delegates to BulkOperationRunner.RunAsync |
SharepointToolbox/Infrastructure/Auth/GraphClientFactory.cs |
Graph SDK client from MSAL | VERIFIED | MsalTokenProvider bridges MSAL PCA to BaseBearerTokenAuthenticationProvider |
SharepointToolbox/Services/BulkSiteService.cs |
Bulk site creation | VERIFIED | Team + Communication site creation via PnP Framework, BulkOperationRunner.RunAsync |
SharepointToolbox/Services/TemplateService.cs |
Site template capture + apply | VERIFIED | SystemListNames filter, recursive folder enumeration, permission group capture, apply creates site + recreates structure |
SharepointToolbox/Services/FolderStructureService.cs |
Folder creation from CSV | VERIFIED | BuildUniquePaths parent-first sort, BulkOperationRunner.RunAsync, Web.Folders.Add |
SharepointToolbox/Services/Export/BulkResultCsvExportService.cs |
Failed items CSV export | VERIFIED | CsvWriter with WriteHeader<T> + Error + Timestamp columns |
SharepointToolbox/Views/Dialogs/ConfirmBulkOperationDialog.xaml |
Pre-write confirmation dialog | VERIFIED | Proceed/Cancel buttons, IsConfirmed property, TranslationSource bindings |
SharepointToolbox/Views/Dialogs/FolderBrowserDialog.xaml |
Library/folder tree browser | VERIFIED | TreeView with lazy-load expansion, library load on Loaded event |
SharepointToolbox/Resources/bulk_add_members.csv |
Example CSV — members | VERIFIED | Present as EmbeddedResource in csproj |
SharepointToolbox/Resources/bulk_create_sites.csv |
Example CSV — sites | VERIFIED | Present as EmbeddedResource in csproj |
SharepointToolbox/Resources/folder_structure.csv |
Example CSV — folder structure | VERIFIED | Present as EmbeddedResource in csproj |
SharepointToolbox/ViewModels/Tabs/TransferViewModel.cs |
Transfer tab ViewModel | VERIFIED | TransferAsync called, GetOrCreateContextAsync for both contexts, ExportFailedCommand |
SharepointToolbox/ViewModels/Tabs/BulkMembersViewModel.cs |
Bulk Members ViewModel | VERIFIED | ParseAndValidateMembers, AddMembersAsync, RetryFailedCommand, ExportFailedCommand, LoadExampleCommand |
SharepointToolbox/ViewModels/Tabs/BulkSitesViewModel.cs |
Bulk Sites ViewModel | VERIFIED | ParseAndValidateSites, CreateSitesAsync, RetryFailedCommand, ExportFailedCommand, LoadExampleCommand |
SharepointToolbox/ViewModels/Tabs/FolderStructureViewModel.cs |
Folder Structure ViewModel | VERIFIED | ParseAndValidateFolders, CreateFoldersAsync, BuildUniquePaths called, ExportFailedCommand |
SharepointToolbox/ViewModels/Tabs/TemplatesViewModel.cs |
Templates ViewModel | VERIFIED | CaptureTemplateAsync, ApplyTemplateAsync, TemplateRepository CRUD, RefreshCommand |
SharepointToolbox/Views/Tabs/TransferView.xaml |
Transfer tab UI | VERIFIED | Source/dest site pickers, library/folder browse buttons, Copy/Move radio, conflict policy, progress, ExportFailed button |
SharepointToolbox/Views/Tabs/BulkMembersView.xaml |
Bulk Members tab UI | VERIFIED | Import/LoadExample buttons, DataGrid with IsValid+Errors columns, RunCommand, RetryFailed, ExportFailed |
SharepointToolbox/Views/Tabs/BulkSitesView.xaml |
Bulk Sites tab UI | VERIFIED | Same pattern as BulkMembers |
SharepointToolbox/Views/Tabs/FolderStructureView.xaml |
Folder Structure tab UI | VERIFIED | DataGrid with Level1-4 columns and Errors column |
SharepointToolbox/Views/Tabs/TemplatesView.xaml |
Templates tab UI | VERIFIED | Capture section with 5 checkboxes, Apply section with title/alias, template DataGrid |
SharepointToolbox/App.xaml.cs |
DI registration for all Phase 4 types | VERIFIED | Lines 124-152: all 5 services, ViewModels, Views registered |
SharepointToolbox/MainWindow.xaml |
5 new tab items | VERIFIED | TransferTabItem, BulkMembersTabItem, BulkSitesTabItem, FolderStructureTabItem, TemplatesTabItem |
Key Link Verification
| From | To | Via | Status | Details |
|---|---|---|---|---|
BulkOperationRunner.cs |
BulkOperationResult.cs |
returns BulkOperationSummary<T> |
WIRED | return new BulkOperationSummary<TItem>(results) on line 34 |
FileTransferService.cs |
BulkOperationRunner.cs |
per-file delegation | WIRED | BulkOperationRunner.RunAsync called on line 33 |
FileTransferService.cs |
MoveCopyUtil |
CSOM file operations | WIRED | MoveCopyUtil.CopyFileByPath (line 85), MoveCopyUtil.MoveFileByPath (line 90) |
BulkMemberService.cs |
BulkOperationRunner.cs |
per-row delegation | WIRED | BulkOperationRunner.RunAsync on line 28 |
GraphClientFactory.cs |
MsalClientFactory |
shared MSAL token | WIRED | _msalFactory.GetOrCreateClient(clientId) in CreateClientAsync |
BulkSiteService.cs |
BulkOperationRunner.cs |
per-site delegation | WIRED | BulkOperationRunner.RunAsync on line 17 |
TemplateService.cs |
SiteTemplate.cs |
builds and returns model | WIRED | SiteTemplate constructed in CaptureTemplateAsync, pattern confirmed |
FolderStructureService.cs |
BulkOperationRunner.cs |
per-folder error handling | WIRED | BulkOperationRunner.RunAsync on line 27 |
CsvValidationService.cs |
CsvHelper |
CsvReader with DetectDelimiter | WIRED | CsvReader with DetectDelimiter = true and detectEncodingFromByteOrderMarks: true |
TemplateRepository.cs |
SiteTemplate.cs |
System.Text.Json serialization | WIRED | JsonSerializer.Serialize/Deserialize<SiteTemplate> |
TransferViewModel.cs |
IFileTransferService.TransferAsync |
RunOperationAsync override | WIRED | _transferService.TransferAsync(srcCtx, dstCtx, job, progress, ct) on line 109 |
TransferViewModel.cs |
ISessionManager.GetOrCreateContextAsync |
context acquisition | WIRED | Called for both srcProfile and dstProfile on lines 106-107 |
BulkMembersView.xaml.cs |
TranslationSource |
localized labels | WIRED | All buttons use TranslationSource.Instance binding |
TemplatesViewModel.cs |
ITemplateService |
capture and apply | WIRED | _templateService.CaptureTemplateAsync (line 112), ApplyTemplateAsync (line 148) |
TemplatesViewModel.cs |
TemplateRepository |
template CRUD | WIRED | _templateRepo.SaveAsync, RenameAsync, DeleteAsync, GetAllAsync all called |
App.xaml.cs |
All Phase 4 services | DI registration | WIRED | AddTransient/AddSingleton for all 10 Phase 4 service types (lines 124-152) |
MainWindow.xaml.cs |
All Phase 4 Views | tab content wiring | WIRED | GetRequiredService<TransferView/BulkMembersView/BulkSitesView/FolderStructureView/TemplatesView>() lines 36-40 |
ConfirmBulkOperationDialog.xaml.cs |
TranslationSource |
localized button text | WIRED | Title and button text bound to bulk.confirm.* keys |
Requirements Coverage
| Requirement | Source Plan | Description | Status | Evidence |
|---|---|---|---|---|
| BULK-01 | 04-03, 04-08 | File/folder transfer between sites with progress tracking | SATISFIED | FileTransferService + TransferViewModel + TransferView. Copy/Move modes, progress bar, cancel, per-file results. |
| BULK-02 | 04-04, 04-09 | Bulk member addition from CSV | SATISFIED | BulkMemberService (Graph + CSOM) + BulkMembersViewModel (CSV import, preview, execute, retry, export) |
| BULK-03 | 04-05, 04-09 | Bulk site creation from CSV | SATISFIED | BulkSiteService (Team + Communication) + BulkSitesViewModel (CSV import, preview, execute) |
| BULK-04 | 04-01, 04-03, 04-04, 04-05, 04-06, 04-08, 04-09 | All bulk operations support cancellation | SATISFIED | BulkOperationRunner.RunAsync propagates OperationCanceledException. Cancel button wired in all 4 Views. |
| BULK-05 | 04-01, 04-03, 04-04, 04-05, 04-06, 04-08, 04-09 | Per-item error reporting (no silent failures) | SATISFIED | BulkItemResult<T>.Failed per item. HasFailures/FailedItems exposed. ExportFailed + RetryFailed in all Views. |
| TMPL-01 | 04-06, 04-10 | Capture site structure as template | SATISFIED | TemplateService.CaptureTemplateAsync captures libraries (filtered), folders (recursive), groups, logo, settings per SiteTemplateOptions |
| TMPL-02 | 04-06, 04-10 | Apply template to create new site | SATISFIED | TemplateService.ApplyTemplateAsync creates Team or Communication site, recreates structure |
| TMPL-03 | 04-02 | Templates persist locally as JSON | SATISFIED | TemplateRepository with atomic write (tmp + Move), JsonSerializer, 6 passing tests |
| TMPL-04 | 04-02, 04-10 | Manage templates (create, rename, delete) | SATISFIED | TemplatesViewModel has CaptureCommand, RenameCommand, DeleteCommand. TemplateRepository has full CRUD. |
| FOLD-01 | 04-06, 04-09 | Folder structure creation from CSV | SATISFIED | FolderStructureService.CreateFoldersAsync with parent-first ordering. FolderStructureViewModel with CSV import, preview, execute. |
| FOLD-02 | 04-07, 04-09 | Example CSV templates provided | SATISFIED | 3 example CSVs in Resources/ as EmbeddedResource. LoadExampleCommand in all 3 CSV ViewModels reads from embedded assembly. |
All 11 requirement IDs accounted for. No orphaned requirements.
Anti-Patterns Found
| File | Observation | Severity | Impact |
|---|---|---|---|
BulkMembersView.xaml |
DataGrid shows IsValid as text column ("True"/"False") but no row-level visual highlighting (no DataGrid.RowStyle + DataTrigger for red background on invalid rows) |
Warning | Invalid rows are identifiable via column text, but visually indistinct. Fix requires adding RowStyle with DataTrigger IsValid=False -> Background=LightCoral. |
BulkSitesView.xaml |
Same as above — no row highlighting for invalid rows | Warning | Same impact |
FolderStructureView.xaml |
Same as above | Warning | Same impact |
No blocker anti-patterns. No TODO/FIXME/placeholder comments in service or ViewModel files. No throw new NotImplementedException. All services have real implementations.
Human Verification Required
1. Application Launch with 5 New Tabs
Test: Run dotnet run --project SharepointToolbox/SharepointToolbox.csproj and count tabs in MainWindow
Expected: 10 visible tabs: Permissions, Storage, Search, Duplicates, Transfer, Bulk Members, Bulk Sites, Folder Structure, Templates, Settings
Why human: WPF application startup, DI resolution, and XAML rendering cannot be verified programmatically
2. Bulk Members — Load Example Flow
Test: Click the "Load Example" button on the Bulk Members tab Expected: DataGrid populates with 7 sample rows (all IsValid = True). PreviewSummary shows "7 rows, 7 valid, 0 invalid" Why human: Requires embedded resource loading, CsvHelper parsing, and DataGrid MVVM binding at runtime
3. Bulk Sites — Semicolon CSV Auto-Detection
Test: Click "Load Example" on Bulk Sites tab Expected: 5 rows parsed correctly despite semicolon delimiter. Name, Alias, Type columns show correct values. Why human: DetectDelimiter behavior requires runtime CsvHelper parsing
4. Invalid Row Display in DataGrid
Test: Import a CSV with one invalid row (e.g., missing email) to Bulk Members Expected: Invalid row visible, IsValid column shows "False", Errors column shows the specific error message Why human: DataGrid rendering requires runtime
5. Confirmation Dialog Before Execution
Test: Load a valid CSV on Bulk Members and click "Add Members"
Expected: ConfirmBulkOperationDialog appears with operation summary and Proceed/Cancel buttons before any SharePoint call is made
Why human: Requires ShowConfirmDialog factory to fire via code-behind at runtime
6. Transfer Tab — Two-Step Browse Flow
Test: On Transfer tab, click Browse for Source; complete the SitePickerDialog; observe FolderBrowserDialog opens Expected: After selecting a site in SitePickerDialog, FolderBrowserDialog opens and loads document libraries from that site Why human: Requires connected tenant and live dialog interaction
7. Templates Tab — Capture Checkboxes
Test: Navigate to Templates tab Expected: Capture section shows 5 checkboxes (Libraries, Folders, Permission Groups, Site Logo, Site Settings), all checked by default Why human: XAML checkbox default state and layout require runtime rendering
Build and Test Summary
Build: dotnet build SharepointToolbox.slnx — Build succeeded, 0 errors
Tests: 122 passed, 22 skipped (all skipped tests require live SharePoint tenant — correctly marked), 0 failed
Key test results:
- BulkOperationRunner: 5/5 pass (all semantics verified including continue-on-error and cancellation)
- CsvValidationService: 9/9 pass (comma + semicolon delimiters, BOM, member/site/folder validation)
- TemplateRepository: 6/6 pass (round-trip JSON, GetAll, Delete, Rename)
- FolderStructureService: 4/5 pass + 1 skip (BuildUniquePaths logic verified; live SharePoint test skipped)
- BulkResultCsvExportService: 2/2 pass (failed-only filtering, Error+Timestamp columns)
Verified: 2026-04-03T00:00:00Z Verifier: Claude (gsd-verifier)