- name
- add-dataverse
- description
- Use when the user wants to add Dataverse tables (existing or new) to a Power Apps mobile app, extend an existing Dataverse table with new columns, or apply an approved data model plan.
- user-invocable
- true
- allowed-tools
- Read, Edit, Write, Grep, Glob, Bash, AskUserQuestion, EnterPlanMode, ExitPlanMode, Task, Skill
- model
- opus
> **Plugin check**: Run `node "${PLUGIN_ROOT}/scripts/check-version.js"` - if it outputs a message, show it to the user before proceeding.
**📋 Shared instructions: [shared-instructions.md](${PLUGIN_ROOT}/shared/shared-instructions.md)** — read first.
# Add Dataverse
**App root:** before any project read or command, execute
[app-working-directory.md](${PLUGIN_ROOT}/shared/references/app-working-directory.md).
Use its resolved absolute `working_dir` for every shell call and file tool,
including referenced commands and delegated skills; never inherit a prior `cd`.
**Entry routing:** use the shared [App feature entry points](../../shared/shared-instructions.md#app-feature-entry-points)
preflight before the workflow below.
**Invocation scope:** follow [Data-source invocation scope](../../shared/shared-instructions.md#data-source-invocation-scope)
before the workflow below. Standalone calls use this skill's own data-delta
approval and verification; do not start a full-app plan or screen workflow.
An existing plan alone does not mean it includes or approves the new request;
never replay the old Data Model instead of resolving the requested delta.
**Removal branch:** after resolving invocation scope, `--remove` or an approved app-binding
removal executes
[data-source-removal.md](../../shared/references/data-source-removal.md), then
returns without entering Steps 1-9. Removing a plan row is not implemented by
re-running table creation or by deleting Dataverse metadata. For a mixed edit,
the owner adds/refreshes first and invokes removal separately after consumer edits.
**Refresh branch:** after resolving invocation scope, `--refresh` or an approved service-only
refresh executes [Refresh a retained source](../../shared/references/data-source-removal.md#refresh-a-retained-source)
and returns before Steps 1-9. Preserve the exact `--data-source-name` and approved
binding identity. Do not replay schema writes, publish, or run `add-data-source`.
Two paths:
- **Existing tables only** — resolve the current scope in Steps 1–2, then skip schema writes and use Step 6 to add only missing approved bindings or refresh approved retained sources.
- **New / extended tables** — full workflow with Web API mutations in dependency order
## Workflow
1. Verify project & auth → 2. Resolve plan/operation manifest → 3. Setup Dataverse Web API auth → 4. Validate manifest or reconcile live metadata → 5. Execute sequential metadata phases → 6. Add data sources → 6b. Publish fallback customizations → 6c. Verify tables → 6d. Write manifest → 7. Inspect generated files → 8. Type-check → 8.5. Offline profile reconciliation → 9. Summary
---
### Step 1 — Verify project & auth
Confirm Power Apps mobile app:
For `--plan-only` or a planning-phase handoff, check the app files read-only and
use the shared proposal-only environment-context rule. Use the same non-persisting
lookup before approval in a normal invocation. Incomplete or conflicting context
returns `NEEDS_CONTEXT` after read-only recovery; never remove the safety flags
or redirect output into configuration to make planning succeed.
```bash
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
if [ ! -f power.config.json ] || [ ! -f app.config.js ]; then
echo "BLOCKED: working_dir is not an initialized app" >&2
exit 1
fi
node "${PLUGIN_ROOT}/scripts/resolve-environment.js" "<selected-environment-id>" --no-cache --require-tenant
```
Read `<selected-environment-id>` from this app's `power.config.json`. Capture the
**environment URL** (for example `https://contoso.crm.dynamics.com`),
**environment ID**, and **tenant ID** from the resolver for Step 3. Use the
owner's absolute `working_dir` for every command and artifact.
### Step 2 — Resolve plan
**Telemetry checkpoint: `resolve_dataverse_schema_plan`**
Look for `native-app-plan.md` in the project root:
```bash
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
test -f native-app-plan.md
```
**Resolve the current request before consuming an existing plan.** A direct
standalone invocation must compare its requested tables/columns/service
changes with the existing plan and read-only live evidence. Present and approve
that exact delta at Step 2.7 after shared planning validation, then save only
the accepted plan changes before implementation.
If the request adds nothing, verify the existing outcome and report a no-op;
do not apply other pending rows. If intent or scope is missing, ask or return
`NEEDS_CONTEXT` without mutation. An explicit request to apply the whole existing
plan still requires approval of the reconciled operation set.
For an approved child call, use only the supplied current `approved_scope`.
In either case, `--plan-only` returns the proposal and STOPs before plan saving,
approval-receipt creation, service generation, or Steps 3–9; a planning-phase
caller has the same proposal-only boundary.
Before reading plan content, inspect `$ARGUMENTS` for the five fast-path
artifact flags in Step 2a. When all are present, only confirm
`native-app-plan.md` exists for hash validation; do not parse its Data Model
section or build operations/service lists from Markdown.
For all non-fast-path Dataverse proposals from `/setup-datamodel`, `/edit-app`, or
a standalone request, including an existing-plan delta, read and execute
[dataverse-change-planning.md](../../shared/references/dataverse-change-planning.md).
Use its scoped evidence/contract for the diagram, architect, or inline path.
An approved child with `planning_snapshot`, `architect_evidence`, and
`schema_contract` instead enters Step 2b to verify that supplied scope; it must
not rediscover or re-approve it. New-binding requests use this planning path;
service-only refreshes and retirements have already returned from their branches.
Older callers may not supply the new structured handoff. Do not assume compact
evidence or complete approval from the caller's name. Without a complete approved
handoff, resolve the current requested delta and use this leaf's normal proposal
and Step 2.7 approval path before execution. Resolve required invocation context
first; a missing or conflicting child root still returns `NEEDS_CONTEXT` before
project access. Never silently replay the saved plan or treat `--skip-planning`
as consent.
An approved legacy child without compact-planning artifacts keeps the supported
Markdown/live-reconciliation path below only when its current request, absolute
root, implementation phase, and exact approved delta are all established.
Never use this legacy path to bypass a partial or invalid compact-planning
handoff, or to skip setup/standalone proposal validation.
**Legacy input only, if present and `<operation_manifest_mode> = fallback`
without a shared scoped contract:** read the
`## Data Model` section. Extract:
- The target reconciliation table (`reuse` / `extend` / `create` / `adapt` / `defer` decisions and evidence)
- The Mermaid ER diagram (informational)
- The "Creation Order" tier list
- Every table referenced by `## Screens`, identity resolution, related-entity fields, forms, dashboards, or shared hooks, including standard reused tables such as `systemuser`, `contact`, and `account`
For legacy callers without scoped planning artifacts, build
`SERVICE_REQUIRED_TABLES` as the union of:
1. every non-deferred row in Target Reconciliation (`reuse`, `extend`, `create`, or `adapt`);
2. every table in Creation Order;
3. every table named by screen/hook data requirements.
Keep this full service inventory as retained context. Only the current approved
delta and its necessary dependencies enter live schema reconciliation. With a
complete approved legacy handoff, proceed to Step 3 after this initialization;
do not repeat proposal Steps 2.5–2.7 or require new compact artifacts from that
owner. A planning-phase caller still returns without implementation.
For the shared scoped path, use the normalized contract's non-deferred
`serviceRequired` declarations instead. Keep the broader existing-app service
inventory as retained context, not a registration or mutation work list.
**Hard rule:** `reuse` means "do not mutate schema"; it does **not** mean "skip generated service." If app code reads or writes a reused table, that table must be in `SERVICE_REQUIRED_TABLES`.
Carry forward any `adapt` (auto-renamed) and `defer` (out-of-scope this run) decisions with their recorded reasons, and apply the alias map to every name you use. A data-modelling conflict never halts this skill — it resolves to `adapt` or `defer` and is reported in Step 9.
This classification belongs to planning: after approval, a changed decision
returns to the owner for revision/approval before any write.
For a scoped handoff, restrict schema writes to the exact `approved_scope` delta;
unaffected plan rows are context, not permission to replay their mutations.
Apply the same restriction to the newly approved standalone request delta.
Retain the full required-service set for existing screens. If reconciliation
would change an approved name, storage target, or screen contract, return the
proposed adaptation to the owner (or standalone user) before writing. Report
dependent consumer changes for separate approval; do not silently rename
underneath the app or treat data approval as permission to edit screens.
**If absent:** check `$ARGUMENTS` for diagram hints (`*.png`, `*.jpg`, `*.jpeg` filename, `erDiagram` keyword, `||--o{` cardinality syntax).
- **Diagram hint present** → Path A (Step 2.5).
- **No hint AND `$ARGUMENTS` describes what the app does** (the typical case) → silently take Path B (Step 2.6 — spawn architect). No prompt.
- **No hint AND `$ARGUMENTS` is empty / non-descriptive** → only then prompt with `AskUserQuestion`:
> "How would you like to define the data model?
> (a) I have an existing ER diagram to upload (PNG/JPG path, Mermaid syntax, or text description)
> (b) Let the data-model-architect agent analyze and propose one (default)
> (c) Cancel — I'll plan it elsewhere first"
Recommend (b), but wait for an explicit answer. Empty input is not approval;
cancellation stops the workflow without planning or mutation. A supplied
description may select the read-only architect path, not approve its result.
With an existing plan and a new request, take the same diagram or architect/
inline proposal path for only that delta, then Step 2.7. Do not skip validation
because a saved plan exists.
#### Step 2a — Approved operation-manifest fast path
When `$ARGUMENTS` supplies all five paths below, record
`<operation_manifest_mode> = candidate`:
- `--schema-contract <working_dir>/.tmp/dataverse-schema-contract.json`
- `--approval-receipt <working_dir>/.tmp/mobile-plan-status.json`
- `--execution-reconciliation <working_dir>/.tmp/dataverse-execution-reconciliation.json`
- `--operation-manifest <working_dir>/.tmp/dataverse-operation-manifest.json`
- `--publish-checkpoint <working_dir>/.tmp/dataverse-publish-pending.json`
Do not reconstruct tables, columns, relationships, keys, payloads, tiers, or
service requirements from Markdown on this path. The gate-owned approval receipt binds
the exact structured contract content/hash, final plan hash, and final
screen/service dependency list; `native-app-plan.md` remains the human review
artifact.
An entirely absent fast-path handoff means
`<operation_manifest_mode> = fallback` and preserves the standalone workflow
below, beginning with Step 2 initialization. A partially supplied handoff, or
a supplied manifest/contract/reconciliation/checkpoint that is malformed, stale,
incomplete, or bound to different context/files, must fail closed: print the
exact validation errors and return control to the orchestrator. Never jump to
Step 4 without Step 2 initialization, partially trust a candidate, or mix its
operations with agent-derived operations.
For an approved implementation candidate, continue to Step 3 and the existing
Step 3c manifest validation; skip Steps 2b and 2.5–2.7. Supplied execution
artifacts never override `--plan-only` or a planning-phase caller: those calls
return the proposal without entering Step 3.
#### Step 2b — Approved scoped planning context
Enter only for a shared compact-planning handoff, not an approved legacy child
or a creation fast-path candidate. The data owner passes the
absolute `planning_snapshot`, `architect_evidence`, and `schema_contract` paths
plus accepted operations, `contract_sha256`, and `plan_sha256` in
`approved_scope`. Require all fields and real implementation approval;
partial, missing, changed, or mismatched context returns `NEEDS_CONTEXT` to
the owner, never a fallback to whole-plan replay or fresh approval inference.
Before Step 3, compare SHA-256 of the contract and final plan file bytes with
those frozen approval hashes, verify the selected target identity, and run the
shared compact-evidence and decision validators against the supplied artifacts.
Do not overwrite either hash to accept changed files. Read mutation intent
from the normalized scoped contract, not historical Markdown rows; retain
unaffected generated services. A proposal-only caller returns without mutation.
After success, keep `<operation_manifest_mode> = fallback` for the existing
standalone execution/reconciliation path, skip Steps 2.5–2.7, and proceed to
Step 3. Planning evidence is not fresh write evidence: Step 4 still reconciles
only this accepted delta and its required dependencies against the live target.
Do not fabricate a `create-mobile-app` receipt or partially supply Step 2a flags.
### Step 2.5 — Path A: Parse user-provided diagram
Used when the user has an existing diagram from another tool (Visio, dbdiagram.io, screenshot, hand-drawn).
Accept three input formats:
| Format | How |
|---|---|
| **Image path** (`*.png` / `*.jpg` / `*.jpeg`) | Use `Read` on the file path. The vision-capable model extracts entities, columns, relationships. |
| **Mermaid syntax** | User pastes a `erDiagram` block in chat. Parse the entities, columns, and `\|\|--o{` cardinalities directly. |
| **Text description** | Parse the requested entities/relationships as intent, then use the shared snapshot-only architect handoff with the text and scoped compact evidence. |
Whichever format, normalize into the same structure used by the planner agent:
```yaml
publisherPrefix: <from detected publisher prefix or user>
tables:
- logicalName: contoso_servicevisit
displayName: Service Visit
status: new # new | extend | reuse
columns: [...]
relationships: [...]
```
Then follow the shared planning workflow to generate `_dm_section.md` with
the Mermaid diagram and a normalized `.tmp/dataverse-schema-contract.json`
from compact evidence. Run its decision validation for the diagram just as for
an architect result, then continue to Step 2.7. Do not save the live plan here.
### Step 2.6 — Path B: Spawn architect agent
If the user picked Path B, use the shared workflow's
`mobile-app:data-model-architect` handoff with `Dataverse planning mode: required`,
the absolute snapshot/compact-evidence paths, the scoped request, and planning/
proposal-only context. Require `_dm_section.md` and the normalized contract.
If the host cannot spawn agents, produce both inline from that same evidence.
Use the shared structured-signal recovery before generic retries. A diagram
parse failure requires resolving the intended entities, not silently inventing
a replacement model.
If they need new tables and refuse both paths, recommend `/setup-datamodel` for
data-only planning and stop. Do not start another workflow without a new request.
### Step 2.7 — Validate and approve the standalone delta
For every new standalone proposal, require exit `0` from
`validate-dataverse-planning-decisions.js` for the current normalized contract
and snapshot before presenting the existing approval gate. This includes
diagrams, architect results, inline fallbacks, and edits to an existing plan.
If either `--plan-only` or a planning-phase caller applies, return the validated
proposal and STOP before saving the live plan or granting execution permission.
Otherwise show the exact delta and its concerns with `EnterPlanMode`. Approval
saves only that delta into `native-app-plan.md`, preserving unaffected sections.
Freeze the shared planning paths, accepted operations, and current
`contract_sha256` / `plan_sha256` as this invocation's approved context; verify
it using Step 2b before implementation. Revision repeats planning validation;
cancellation stops without saving or mutation. Already-approved scoped child
calls and valid creation fast-path calls do not repeat this gate.
### Step 3 — Setup Dataverse Web API auth
Required only after implementation approval. For approved existing-table
bindings with no schema mutation, skip to Step 6, not the schema-write phases.
#### Step 3a — Environment consistency check
The Power Apps CLI (`$PA`) and `az` authenticate independently — they can point to different accounts. Verify `power.config.json` resolves and `az` can token for the target tenant before making any Dataverse API calls:
```bash
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
ENVIRONMENT_ID=$(node -p "require('./power.config.json').environmentId") || exit 1
ENV_JSON=$(node "${PLUGIN_ROOT}/scripts/resolve-environment.js" "$ENVIRONMENT_ID") || exit 1
echo "$ENV_JSON"
az account show --query "{user: user.name, tenant: tenantId}" -o json
```
Compare the resolved environment URL with `<envUrl>` captured in Step 1. If they differ, **STOP** and warn:
> "⚠️ Environment mismatch detected:
> - resolver reports: `<resolved_env_url>`
> - This project targets: `<envUrl>`
>
> The Dataverse API token comes from `az`, which must target the same tenant as the selected environment. Run:
> ```bash
> cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
> az login --tenant <tenant-id> # switch az to the right tenant
> ```
> Then re-run `/add-dataverse`."
**Do NOT proceed with table creation if environments don't match** — you'll create tables in the wrong org.
#### Step 3b — Acquire token
```bash
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
az account show --query "user.name" -o tsv
```
If empty, instruct `az login` and stop.
**Script invocation contract — read this once, all subsequent calls in this skill follow it:**
```bash
cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
node "${PLUGIN_ROOT}/scripts/dataverse-request.js" <envUrl> <METHOD> <apiPath> \
[--body '<json>'] [--include-headers] \
--tenant-id '<tenantId-from-resolve-environment>'
```
- Three positional args, in order: `<envUrl>`, `<METHOD>` (GET / POST / PATCH / DELETE), `<apiPath>` (everything after `/api/data/v9.2/`).
View on GitHub