Skip to content

Commit 8f71361

Browse files
joshsmithxrmclaude
andcommitted
feat: migrate interactive mode to Terminal.Gui TUI framework
- Add Terminal.Gui 1.19 NuGet package for TUI rendering - Create TUI application structure (PpdsApplication, MainWindow, SqlQueryScreen) - Create QueryResultsTableView with pagination, copy, and URL features - Extract QueryResultConverter for testable value formatting logic - Update InteractiveCommand to launch Terminal.Gui app Architecture documentation (ADRs): - ADR-0024: Shared Local State Architecture (~/.ppds/ for all UIs) - ADR-0025: UI-Agnostic Progress Reporting (IProgressReporter pattern) - ADR-0026: Structured Error Model (PpdsException hierarchy) Updates CLAUDE.md with Platform Architecture section explaining the multi-interface platform (CLI, TUI, VS Code extension). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 404a1d7 commit 8f71361

File tree

12 files changed

+2091
-19
lines changed

12 files changed

+2091
-19
lines changed

CLAUDE.md

Lines changed: 298 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@ NuGet packages & CLI for Power Platform: plugin attributes, Dataverse connectivi
66

77
| Rule | Why |
88
|------|-----|
9-
| Commit directly to main | Protected branch; always create branch + PR |
10-
| Regenerate `PPDS.Plugins.snk` | Breaks strong naming |
11-
| Create new ServiceClient per request | 42,000x slower than pool |
12-
| Hold single pooled client for multiple queries | Defeats pool parallelism |
13-
| Use magic strings for generated entities | Use `EntityLogicalName` and `Fields.*` |
14-
| Write CLI status messages to stdout | stdout = data, stderr = status |
9+
| Commit directly to `main` | Branch is protected; all changes require PR |
10+
| Regenerate `PPDS.Plugins.snk` | Breaks strong naming; existing assemblies won't load |
11+
| Skip XML documentation on public APIs | Consumers need IntelliSense documentation |
12+
| Commit with failing tests | All tests must pass before merge |
13+
| Create new ServiceClient per request | 42,000x slower than Clone/pool pattern |
14+
| Guess parallelism values | Use `RecommendedDegreesOfParallelism` from server |
15+
| Hold single pooled client for multiple queries | Defeats pool parallelism; see `.claude/rules/DATAVERSE_PATTERNS.md` |
16+
| Use magic strings for generated entities | Use `EntityLogicalName` and `Fields.*` constants |
17+
| Use late-bound `Entity` for generated entity types | Use early-bound classes; compile-time safety |
18+
| Write CLI status messages to stdout | Use `Console.Error.WriteLine` for status; stdout is for data |
19+
| Access `~/.ppds/` files directly from UI code | Use Application Services; they handle caching, locking (ADR-0024) |
20+
| Implement data/business logic in UI layer | UIs are dumb views; logic belongs in Application Services |
21+
| Write progress directly to console from services | Accept `IProgressReporter`; let UI render (ADR-0025) |
22+
| Throw raw exceptions from Application Services | Wrap in `PpdsException` with ErrorCode/UserMessage (ADR-0026) |
1523

1624
## ALWAYS
1725

@@ -20,20 +28,97 @@ NuGet packages & CLI for Power Platform: plugin attributes, Dataverse connectivi
2028
| Use connection pool for multi-request scenarios | See `.claude/rules/DATAVERSE_PATTERNS.md` |
2129
| Use bulk APIs (`CreateMultiple`, `UpdateMultiple`) | 5x faster than `ExecuteMultiple` |
2230
| Add new services to `RegisterDataverseServices()` | Keeps CLI and library DI in sync |
31+
| Use Application Services for all persistent state | Single code path for CLI/TUI/RPC (ADR-0024) |
32+
| Accept `IProgressReporter` for operations >1 second | All UIs need feedback for long operations (ADR-0025) |
33+
| Include ErrorCode in `PpdsException` | Enables programmatic handling (retry, re-auth) (ADR-0026) |
34+
| Make new user data accessible via `ppds serve` | VS Code extension needs same data as CLI/TUI |
2335

24-
## Structure
36+
---
2537

38+
## 💻 Tech Stack
39+
40+
| Technology | Version | Purpose |
41+
|------------|---------|---------|
42+
| .NET | 4.6.2, 8.0, 9.0, 10.0 | Plugins: 4.6.2 only; libraries/CLI: 8.0+ |
43+
| C# | Latest (LangVersion) | Primary language |
44+
| Strong Naming | .snk file | Required for Dataverse plugin assemblies |
45+
| Terminal.Gui | 1.19+ | TUI application framework |
46+
| Spectre.Console | 0.54+ | CLI command output |
47+
48+
---
49+
50+
## 📁 Project Structure
51+
52+
```
53+
ppds-sdk/
54+
├── src/
55+
│ ├── PPDS.Plugins/ # Plugin attributes (PluginStep, PluginImage)
56+
│ ├── PPDS.Dataverse/ # Connection pool, bulk operations, metadata
57+
│ │ └── Generated/ # Early-bound entity classes (DO NOT edit)
58+
│ ├── PPDS.Migration/ # Migration engine library
59+
│ ├── PPDS.Auth/ # Authentication profiles
60+
│ └── PPDS.Cli/ # CLI tool (ppds command)
61+
│ ├── Commands/ # CLI command handlers
62+
│ ├── Services/ # Application Services (ADR-0015)
63+
│ └── Tui/ # Terminal.Gui application
64+
├── tests/ # Unit, integration, and live tests
65+
├── docs/adr/ # Architecture Decision Records
66+
└── CHANGELOG.md
67+
```
68+
69+
## 🏛️ Platform Architecture
70+
71+
PPDS is a **multi-interface platform**, not just a CLI tool. The TUI is the primary development interface, with VS Code extension and other frontends consuming the same services.
72+
73+
```
74+
┌─────────────────────────────────────────────────────────────┐
75+
│ User Interfaces │
76+
├───────────────┬───────────────┬───────────────┬─────────────┤
77+
│ CLI Commands │ TUI App │ VS Code Ext │ Future │
78+
│ (ppds data) │ (ppds -i) │ (RPC client) │ (Web, etc) │
79+
│ │ │ │ │
80+
│ Spectre.Console│ Terminal.Gui │ JSON-RPC │ │
81+
├───────────────┴───────────────┴───────────────┴─────────────┤
82+
│ ppds serve (RPC Server) │
83+
│ Long-running service for extensions │
84+
├─────────────────────────────────────────────────────────────┤
85+
│ Application Services Layer (ADR-0015) │
86+
│ ISqlQueryService, IDataMigrationService, IPluginService │
87+
│ • Accepts IProgressReporter (ADR-0025) │
88+
│ • Throws PpdsException (ADR-0026) │
89+
│ • Reads/writes ~/.ppds/ (ADR-0024) │
90+
├─────────────────────────────────────────────────────────────┤
91+
│ PPDS.Dataverse / PPDS.Migration / PPDS.Auth │
92+
└─────────────────────────────────────────────────────────────┘
2693
```
27-
src/
28-
├── PPDS.Plugins/ # Plugin attributes (PluginStep, PluginImage)
29-
├── PPDS.Dataverse/ # Connection pool, bulk operations
30-
│ └── Generated/ # Early-bound entities (DO NOT edit)
31-
├── PPDS.Migration/ # Migration engine
32-
├── PPDS.Auth/ # Auth profiles
33-
└── PPDS.Cli/ # CLI (ppds command)
94+
95+
### Design Principles
96+
97+
| Principle | Implication |
98+
|-----------|-------------|
99+
| **TUI-first** | Build features in TUI first, then expose via RPC for extensions |
100+
| **Service layer** | All business logic in Application Services, never in UI code |
101+
| **Shared local state** | All UIs access same `~/.ppds/` data via services (ADR-0024) |
102+
| **Framework choice** | CLI: Spectre.Console, TUI: Terminal.Gui, Extension: RPC client |
103+
104+
### Shared Local State
105+
106+
All user data lives in `~/.ppds/` and is accessed via Application Services:
107+
34108
```
109+
~/.ppds/
110+
├── profiles.json # Auth profiles (IProfileService)
111+
├── history/ # Query history per-environment (IQueryHistoryService)
112+
├── settings.json # User preferences (ISettingsService)
113+
├── msal_token_cache.bin # MSAL token cache
114+
└── ppds.credentials.dat # Encrypted credentials
115+
```
116+
117+
**Access pattern:** `CLI/TUI/VSCode → Application Service → ~/.ppds/`
118+
119+
---
35120

36-
## Generated Entities
121+
## 🏗️ Generated Entities
37122

38123
Early-bound in `src/PPDS.Dataverse/Generated/`. Use `EntityLogicalName` and `Fields.*` constants.
39124

@@ -51,7 +136,204 @@ Key: Get client INSIDE parallel loops. Use `pool.GetTotalRecommendedParallelism(
51136

52137
MinVer tags: `{Package}-v{version}` (e.g., `Cli-v1.0.0-beta.11`)
53138

54-
## Commands
139+
## 🛠️ Common Commands
140+
141+
```powershell
142+
dotnet build # Debug build
143+
dotnet build -c Release # Release build
144+
dotnet test # Run all tests
145+
dotnet pack -c Release -o ./nupkgs
146+
```
147+
148+
---
149+
150+
## 🔄 Development Workflow
151+
152+
1. Create feature branch from `main`
153+
2. Make changes + **add tests for new classes**
154+
3. Update `CHANGELOG.md` (same commit)
155+
4. Run `/pre-pr` before committing
156+
5. Create PR to `main`
157+
6. Run `/review-bot-comments` after bots comment
158+
159+
### Plan Mode Checklist
160+
161+
- [ ] **Shared utilities identified** - Will logic be needed in multiple files?
162+
- [ ] **Constants centralized** - Magic numbers/strings that should be shared?
163+
- [ ] **Existing patterns checked** - Similar functionality to extend?
164+
165+
**Anti-pattern:** Planning WHAT without WHERE. Always consider where shared logic should live to avoid expensive refactoring.
166+
167+
### Code Conventions
168+
169+
- Use nullable reference types (`string?` not `string`)
170+
- XML documentation on public APIs
171+
- Comments explain WHY, not WHAT
172+
- Namespaces: `PPDS.{Package}.{Area}` (e.g., `PPDS.Auth.Credentials`)
173+
174+
---
175+
176+
## 📦 Version Management
177+
178+
Each package has independent versioning using MinVer:
179+
180+
| Package | Tag Format |
181+
|---------|------------|
182+
| PPDS.Plugins | `Plugins-v{version}` |
183+
| PPDS.Dataverse | `Dataverse-v{version}` |
184+
| PPDS.Migration | `Migration-v{version}` |
185+
| PPDS.Auth | `Auth-v{version}` |
186+
| PPDS.Cli | `Cli-v{version}` |
187+
188+
Pre-release: `-alpha.N`, `-beta.N`, `-rc.N` suffix
189+
190+
---
191+
192+
## 🔀 Git Strategy
193+
194+
| Branch | Purpose |
195+
|--------|---------|
196+
| `main` | Protected, always releasable |
197+
| `feature/*` | New features |
198+
| `fix/*` | Bug fixes |
199+
200+
**Merge:** Squash merge to main. Pre-release = fix pattern issues now, don't defer.
201+
202+
---
203+
204+
## 🚀 Release Process
205+
206+
1. Update per-package `CHANGELOG.md` (in `src/{package}/`)
207+
2. Merge to `main`
208+
3. Create GitHub Release with package-specific tag
209+
4. `publish-nuget.yml` workflow publishes to NuGet.org
210+
211+
---
212+
213+
## ⚡ Dataverse Performance
214+
215+
**See `.claude/rules/DATAVERSE_PATTERNS.md` for pool usage patterns, DOP parallelism, and code examples.**
216+
217+
Key points:
218+
- Get client INSIDE parallel loops (not outside)
219+
- Use `pool.GetTotalRecommendedParallelism()` as DOP ceiling
220+
- Reference impl: `BulkOperationExecutor.cs`
221+
222+
### Architecture Decision Records
223+
224+
| ADR | Summary |
225+
|-----|---------|
226+
| [0001](docs/adr/0001_DISABLE_AFFINITY_COOKIE.md) | Disable affinity cookie for 10x throughput |
227+
| [0002](docs/adr/0002_MULTI_CONNECTION_POOLING.md) | Multiple Application Users multiply API quota |
228+
| [0003](docs/adr/0003_THROTTLE_AWARE_SELECTION.md) | Route away from throttled connections |
229+
| [0004](docs/adr/0004_THROTTLE_RECOVERY_STRATEGY.md) | Transparent throttle waiting |
230+
| [0005](docs/adr/0005_DOP_BASED_PARALLELISM.md) | DOP-based parallelism |
231+
| [0006](docs/adr/0006_CONNECTION_SOURCE_ABSTRACTION.md) | IConnectionSource for custom auth |
232+
| [0007](docs/adr/0007_UNIFIED_CLI_AND_AUTH.md) | Unified CLI and auth profiles |
233+
| [0008](docs/adr/0008_CLI_OUTPUT_ARCHITECTURE.md) | CLI stdout/stderr separation |
234+
| [0009](docs/adr/0009_CLI_COMMAND_TAXONOMY.md) | CLI command taxonomy |
235+
| [0010](docs/adr/0010_PUBLISHED_UNPUBLISHED_DEFAULT.md) | Default to published content |
236+
| [0011](docs/adr/0011_DEPLOYMENT_SETTINGS_FORMAT.md) | Deployment settings format |
237+
| [0012](docs/adr/0012_HYBRID_FILTER_DESIGN.md) | Hybrid filter design |
238+
| [0013](docs/adr/0013_CLI_DRY_RUN_CONVENTION.md) | CLI --dry-run convention |
239+
| [0014](docs/adr/0014_CSV_MAPPING_SCHEMA.md) | CSV mapping schema |
240+
| [0015](docs/adr/0015_APPLICATION_SERVICE_LAYER.md) | Application service layer for CLI/TUI/Daemon |
241+
| [0019](docs/adr/0019_POOL_MANAGED_CONCURRENCY.md) | Pool-managed concurrency |
242+
| [0020](docs/adr/0020_IMPORT_ERROR_REPORTING.md) | Import error reporting |
243+
| [0021](docs/adr/0021_TRUNCATE_COMMAND.md) | Truncate command |
244+
| [0022](docs/adr/0022_IMPORT_DIAGNOSTICS_ARCHITECTURE.md) | Import diagnostics architecture |
245+
| [0023](docs/adr/0023_CLI_BINARY_RELEASE_PROCESS.md) | CLI binary release process |
246+
| [0024](docs/adr/0024_SHARED_LOCAL_STATE.md) | Shared local state for multi-UI |
247+
| [0025](docs/adr/0025_UI_AGNOSTIC_PROGRESS.md) | UI-agnostic progress reporting |
248+
| [0026](docs/adr/0026_STRUCTURED_ERROR_MODEL.md) | Structured error model |
249+
250+
---
251+
252+
## 🖥️ CLI (PPDS.Cli)
253+
254+
See [CLI README](src/PPDS.Cli/README.md) for full documentation.
255+
256+
Quick start:
257+
```bash
258+
ppds auth create --name dev # Create profile
259+
ppds env select # Select environment
260+
ppds data export --schema schema.xml --output data.zip
261+
```
262+
263+
**Output conventions:** stdout = data, stderr = status messages.
264+
265+
---
266+
267+
## 🔌 DI Registration
268+
269+
Two DI paths must stay synchronized:
270+
- **Library:** `AddDataverseConnectionPool(config)`
271+
- **CLI:** `ProfileServiceFactory.CreateFromProfileAsync()`
272+
273+
Both call `RegisterDataverseServices()` to register shared services. When adding a service, add it there (not to individual paths).
274+
275+
---
276+
277+
## 🧪 Testing
278+
279+
**See `.claude/rules/TESTING.md` for test categories, CI behavior, and local setup.**
280+
281+
Key rules:
282+
- New public class → must have test class
283+
- Run `/run-integration-local` for live tests
284+
- CI: Unit tests on commits, full tests on PRs
285+
286+
---
287+
288+
## 🤖 Bot Review Handling
289+
290+
PRs reviewed by Copilot, Gemini, CodeQL. Not all findings are valid.
291+
292+
| Finding Type | Action |
293+
|--------------|--------|
294+
| Unused code, resource leaks | Usually valid - fix |
295+
| Style suggestions | Often preference - dismiss with reason |
296+
| Logic errors | Verify manually |
297+
298+
Run `/review-bot-comments [PR#]` to triage.
299+
300+
---
301+
302+
## 🗺️ Issue Triage
303+
304+
Issues are tracked in the [PPDS Roadmap](https://github.com/users/joshsmithxrm/projects/3) project.
305+
306+
### Triage Command
307+
308+
`/triage` - Systematically categorize GitHub issues with project fields and labels
309+
310+
### Project Fields
311+
312+
| Field | Values | Purpose |
313+
|-------|--------|---------|
314+
| **Type** | feature, bug, chore, docs, refactor | Work category |
315+
| **Priority** | P0-Critical, P1-High, P2-Medium, P3-Low | Urgency/importance |
316+
| **Size** | XS, S, M, L, XL | Effort estimate |
317+
| **Status** | Todo, In Progress, Done | Current state |
318+
| **Target** | This Week, Next, Q1 2026, CLI v1.0.0, Blocked | Sequencing |
319+
| **Parent Issue** | Link to epic/parent | Issue hierarchy |
320+
321+
### Label Strategy
322+
323+
**Use labels for:**
324+
- **Epics**: epic:cli-daemon, epic:testing, epic:data-migration
325+
- **Phases**: phase:1-core, phase:2-connections, phase:3-traces, phase:4-webresources, phase:5-migration
326+
- **Areas**: area:plugins, area:data, area:auth, area:cli, area:tui, area:pooling, area:daemon
327+
- **Special**: foundation, blocked, performance
328+
- **Type hints** (optional): bug, enhancement, documentation
329+
330+
**Don't use labels for:** Priority, Size, Status, Target (use project fields instead)
331+
332+
See [docs/ROADMAP.md](docs/ROADMAP.md) for detailed guidelines.
333+
334+
---
335+
336+
## 🛠️ Claude Commands
55337
56338
| Command | Purpose |
57339
|---------|---------|

0 commit comments

Comments
 (0)