---
name: build-dataverse-app
description: Builds Dataverse schema, forms, views, security roles, and MDA app. Use when creating tables, columns, relationships, or building any Dataverse app components. Handles DDL lock sequencing and OOB entity checks.
argument-hint: "[ado-project] [environment-name-or-url] [optional: --project-dir DIR] [optional: --epic-id N] [optional: --dry-run]"
---

# Build Dataverse App from ADO Requirements

Read ADO User Stories and execute the corresponding Dataverse MCP operations to build the actual app. Every Dataverse action traces back to a specific ADO work item — no operations happen outside of ADO traceability.

## Arguments

- `$ARGUMENTS[0]` — ADO project name (e.g., `ADO MCP Demo`)
- `$ARGUMENTS[1]` — Dataverse environment URL or name (e.g., `https://org209ecb48.crm.dynamics.com`)
- `--project-dir DIR` — Project runs directory (e.g., `./project-runs/sba-cash-app/`). Contains `ado-work-items.json` and `build-specification.json`.
- `--epic-id N` — Process only a specific Epic (by ADO work item ID)
- `--dry-run` — Report what would be done without executing

## Pipeline Integration

When invoked as **Phase 4** of the `/project-standup` pipeline:

**Input:**
- `ado-work-items.json` — ADO work item ID mapping from Phase 3
- `build-specification.json` — Architecture spec from Phase 2

**Output:**
- Updated `ado-work-items.json` — States updated as items are completed
- `build-log.json` — Action log of all Dataverse operations

### Tandem Execution Pattern

**Every Dataverse action MUST be tied to an ADO work item.** The pattern for each item:

```
1. Read item from ado-work-items.json → get work_item_id, category
2. Query ADO: wit_get_work_item(id) → read Implementation Details
3. Update ADO: wit_update_work_item(id, state: "Active")
4. Execute Dataverse MCP operations per the story's specs
5. Update ADO: wit_update_work_item(id, state: "Closed")
6. Add comment: wit_add_work_item_comment(id, "Completed: [summary]")
7. Update ado-work-items.json → set state: "Closed"
8. Append to build-log.json
```

When using batch Dataverse tools (e.g., `bulk_create_tables` for multiple tables), update ALL corresponding ADO items for that batch in one pass after the batch succeeds.

## Prerequisites

- ADO backlog must exist with Epics → Features → User Stories (created by `/create-ado-work-items`)
- Dataverse environment must be accessible via MCP
- Publisher must exist in the target environment
- `ado-work-items.json` must exist in the project directory (or Phase 0 will build it)

## Phase 1: INVENTORY

1. Read `ado-work-items.json` from the project directory (if available) — this is the preferred source
   - If not available: Query ADO for all Epics with tag `Requirements-Extraction` (or specified `--epic-id`), build the mapping
2. For each Epic, get child Features and their child User Stories
3. Categorize each story by actionability:
   - **AUTO**: Can be fully executed via Dataverse MCP (tables, columns, forms, views, security roles, app, sitemap)
   - **PARTIAL**: Can be partially executed (dashboards, simple business rules)
   - **MANUAL**: Requires manual work or other tools (Power Automate flows, D365 integration, BPFs, complex business rules)
   - **FLOW**: Power Automate flows — deferred to Phase 5 (`/build-power-automate-flows`)
4. Read `build-specification.json` (if available) for validated schema details — form layouts, view columns, field types, security privileges
5. Show inventory to user and confirm

## Phase 2: SETUP

1. Create or identify the solution and publisher in the target environment
2. Create the Model-Driven App shell
3. This maps to the "Infrastructure Setup" Epic

## Phase 3: BUILD (Epic-by-Epic)

Execute in this dependency order:

### Step 1: Core Data Model (Tables)
For each "Create [table] table" story:
- `dataverse_create_table` with schema_name, display_name, plural_name, primary_column
- Add to solution
- Update ADO story state → "Active" when starting, "Closed" when done

### Step 2: Columns & Relationships
For each table, create all columns mentioned in the story's Implementation Details:
- `dataverse_bulk_create_columns` for efficiency
- `dataverse_create_one_to_many` / `dataverse_create_many_to_many` for relationships
- `dataverse_add_choice_option` for option sets
- Update ADO stories

### Step 3: Views
For each "Create system views" story:
- `dataverse_create_view` with columns and filters from the story
- Update ADO stories

### Step 4: Forms
For each form-related story:
- `dataverse_build_form` for main forms
- `dataverse_add_form_fields`, `dataverse_add_form_tab`, `dataverse_add_form_section` for modifications
- Update ADO stories

### Step 5: Security
For each security role story:
- `dataverse_create_security_role`
- `dataverse_add_role_privilege` for table permissions
- Update ADO stories

### Step 6: App & Sitemap
- `dataverse_update_sitemap` to add all tables to navigation
- `dataverse_update_app` to include all tables
- `dataverse_publish_all`

### Step 7: Mark Manual Items
For stories categorized as MANUAL or PARTIAL:
- Update ADO story with a comment explaining what was/wasn't done
- Set state to "New" with tag "Manual-Action-Required"

## Phase 4: VERIFY & REPORT

1. `dataverse_publish_all` — make everything live
2. Query the environment to verify:
   - `dataverse_list_tables` — confirm all tables exist
   - `dataverse_list_columns` on key tables — confirm columns
   - `dataverse_list_views` — confirm views
   - `dataverse_list_forms` — confirm forms
   - `dataverse_list_security_roles` — confirm roles
   - `dataverse_get_app` — confirm app
3. Report summary: items built, items skipped, items needing manual work
4. Update ADO Epic/Feature states based on child completion
5. Write `build-log.json` to project directory:
   ```json
   {
     "completed_at": "ISO timestamp",
     "actions": [
       {
         "ado_item_id": 1236,
         "title": "Create Application table",
         "action": "dataverse_create_table",
         "result": "success",
         "details": "Table mcpd_Application created in SBACashApp solution",
         "timestamp": "ISO"
       }
     ],
     "summary": {
       "tables_created": 12,
       "columns_created": 145,
       "views_created": 24,
       "forms_created": 12,
       "security_roles_created": 3,
       "app_created": true,
       "items_auto_completed": 45,
       "items_manual_remaining": 8,
       "items_flow_deferred": 5
     }
   }
   ```

## Execution Strategy

### For large projects (50+ stories), use parallel subagents:
- **Agent per Epic**: Each agent reads its Epic's stories from ADO, executes Dataverse calls, updates ADO
- **Dependency awareness**: Core Data Model must complete before Forms/Views agents start
- **Batch operations**: Use `dataverse_bulk_create_tables`, `dataverse_bulk_create_columns` where possible

### ADO Update Pattern
When starting a story: `wit_update_work_item` → state: "Active"
When completing a story: `wit_update_work_item` → state: "Closed"
When skipping (manual): `wit_add_work_item_comment` with reason, add tag "Manual-Action-Required"

## Table Naming Convention

Schema names follow the publisher prefix pattern:
- Publisher prefix: `{prefix}_` (e.g., `mcpd_`)
- Table schema: `{prefix}_{TableName}` (e.g., `mcpd_Payment`)
- Column schema: `{prefix}_{ColumnName}` (e.g., `mcpd_PaymentAmount`)

When the source requirements specify a schema name (e.g., `dv_Payments`), map it to the publisher prefix:
- `dv_Payments` → `{prefix}_Payment`
- `ca_AuditLog` → `{prefix}_AuditLog`
- `CashApp_BatchHeader` → `{prefix}_BatchHeader`

## Environment Parameters

For all Dataverse MCP calls, pass:
- `environment_url`: the target environment URL
- `tenant_id`: the Azure AD tenant ID
- `solution_name`: the solution unique name (all components added to this solution)

## CRITICAL: ADO Stories Must Drive ALL Work

The Dataverse builder **MUST** read and follow ADO work item specifications — not infer what to build from Dataverse metadata or hierarchy plans alone. Every Dataverse operation traces to a specific ADO story.

### Phase 0: Build the ADO Mapping File

Before ANY Dataverse work begins, the orchestrator MUST:

1. Query ADO for all Epics under the project (use `wit_query_work_items` with tag `Requirements-Extraction`)
2. For each Epic → Features → Stories, capture: `{ story_title, work_item_id, state, epic_title, feature_title }`
3. Write this mapping to `extraction-results/_ado_story_map.json`:
   ```json
   {
     "project": "ADO MCP Demo",
     "created_at": "ISO timestamp",
     "stories": [
       {
         "id": 1234,
         "title": "Create dv_Payments table",
         "state": "New",
         "epic": "Core Data Model",
         "feature": "Payment & Remittance Tables",
         "category": "Tables"
       }
     ]
   }
   ```
4. This file is the **single source of truth** — every subagent reads it, every ADO update writes back to it

### Why This Matters

- **Context compaction**: Long-running builds will hit context limits. If story IDs are only in memory, they're lost. The mapping file survives compaction.
- **Traceability**: Every `dataverse_create_table` call should be traceable to a specific ADO story ID
- **ADO updates**: Agents need story IDs to call `wit_update_work_item` (state changes) and `wit_add_work_item_comment` (completion notes)
- **Resume capability**: If a build is interrupted, the mapping file shows which stories are "Closed" (done) vs "New" (pending)

### Agent ADO Loop Pattern

Every builder subagent follows this pattern for each story it processes:

```
1. Read story from _ado_story_map.json → get work_item_id
2. Query ADO: wit_get_work_item(id) → read Implementation Details, Acceptance Criteria
3. Update ADO: wit_update_work_item(id, state: "Active")
4. Execute Dataverse operations per the story's specs
5. Update ADO: wit_update_work_item(id, state: "Closed")
6. Add comment: wit_add_work_item_comment(id, "Completed via Dataverse MCP: [summary of what was built]")
7. Update _ado_story_map.json → set state: "Closed"
```

Do NOT:
- Build from hierarchy plans or column metadata alone (specs are in the ADO stories)
- Skip ADO reads because "you already know what to build" (stories have specific field placements, view columns, form layouts)
- Defer ADO updates to a reconciliation pass at the end (update as you go)

## Lessons Learned

### Dataverse MCP Tool Gotchas
- `dataverse_bulk_create_columns` is much faster than individual `dataverse_create_column` calls
- `dataverse_build_form` can create a complete form in one call (tabs, sections, fields)
- `dataverse_build_form` fields MUST be objects: `{"logical_name": "mcpd_name"}` — plain strings like `"mcpd_name"` will be silently skipped with "(missing logical_name)"
- Always `dataverse_publish_all` after major batches of changes
- Option set values must use the publisher's option_value_prefix range
- Lookup columns require the target table to exist first — create all tables before relationships
- Forms need tables+columns to exist first — create forms after columns

### Performance & Efficiency
- Do NOT add `sleep` commands between Dataverse MCP calls — the MCP server handles 429 throttling and retries internally. Preemptive sleeps waste time and add no value.
- Use `dataverse_bulk_create_tables` and `dataverse_bulk_create_columns` wherever possible for batch efficiency
- Security role privilege creation (`add_role_privilege`) is high-volume (6 privileges × N tables per role) — let the MCP handle retries, don't add delays
- Parallel subagents work well: tables agent, relationships agent, views agent, security agent can run concurrently after tables+columns are done

### Pipeline Integration
- The mapping file (`_ado_story_map.json`) MUST be created before build starts and updated as work completes
- If context compaction occurs mid-build, agents can resume by reading the mapping file to see what's done vs pending
- Forms, sitemap structure, and view definitions are all specified in ADO stories — don't improvise these from column metadata
- Manual/partial stories (flows, BPFs, D365 integrations) should be tagged and commented in ADO, not silently skipped

### Pilot Test Results (SBA Cash App, 2026-03-10)
- **Environment**: Sandbox_AI-PoCs (`org209ecb48.crm.dynamics.com`)
- **Solution**: `SBACashApp` (publisher: `mcpd`)
- **Scale**: 84 tables, ~879 columns, 26 relationships, 12 views, 5 custom forms, 3 security roles (620+ privileges), 1 MDA app
- **What worked well**: Parallel agents for tables/columns (pre-compaction), parallel agents for relationships/views/security (post-compaction), bulk column creation
- **What broke**: After context compaction, lost ADO story IDs → fell back to ad-hoc form/sitemap building instead of story-driven. This is the primary reason for the Phase 0 mapping file requirement above.
- **Timing**: ~45 min for tables+columns, ~7 min for relationships, ~7 min for views, ~7 min for security roles, ~2 min for forms, ~1 min for app creation
