microsoft/genpage
Creates, updates, and deploys Power Apps generative pages for model-driven apps using React v17, TypeScript, and Fluent UI V9. Orchestrates specialist agents for planning, entity creation, and code generation. Use it when user asks to build, retrieve, or update a page in an existing Microsoft Power Apps model-driven app. Use it when user mentions "generative page", "page in a model-driven", or "genux". This skill stands alone and does not require /app-builder — but if the user wants a whole app built (tables, forms, views, sitemap) rather than pages for an app that already exists, use /app-builder instead.
npx skills add https://github.com/microsoft/power-platform-skills --skill genpage
Triggers: genpage, generative page, create genpage, genux page, build genux, power apps page, model page
Keywords: power apps, generative pages, genux, model-driven, dataverse, react, fluent ui, pac cli
Aliases: /genpage, /gen-page, /genux
This skill orchestrates specialist agents across the create and edit flows:
Create flow:
genpage-planner — validates prerequisites, gathers requirements, detects whatentities and apps exist, presents a plan for approval, writes genpage-plan.md.
May pause and return connector_discovery_required (see 2).
genpage-connector-builder — top-level orchestrator dispatch for connectorfeature-gating and discovery; writes connector-bindings.md + connectors.json.
Runs after the planner has resolved create-vs-edit and the target environment
(discovery is mutating), then the planner is re-invoked with its contract.
genpage-entity-builder — creates Dataverse entities (tables, columns,relationships, choices, sample data) via the plugin's Node.js Web API scripts
genpage-page-builder — generates one complete .tsx file per page; multiplebuilders run in parallel for multi-page requests
Edit flow:
genpage-connector-builder — top-level orchestrator dispatch when an edit adds,replaces, discovers, or clears connector bindings; preserves unchanged bindings
when the feature gate is OFF
genpage-edit-planner — reads the downloaded page artifacts, gathers changerequirements, presents an edit plan, writes genpage-edit-plan.md
You (the skill) coordinate the agents and own connector dispatch, app creation,
RuntimeTypes generation, deployment, browser verification, and the inline
application of planned edits.
@fluentui/react-components exclusively (DatePicker from @fluentui/react-datepicker-compat, TimePicker from @fluentui/react-timepicker-compat).tsx file100vh/100vwFollow these phases in order for every /genpage invocation.
Derive a short folder name from the user's requirements:
$ARGUMENTScandidate-tracker)mkdir -p <folder-name>Write package.json and genpage.d.ts into the working directory so the
developer can npm install and get IntelliSense, type-checking, and "go to
definition" in their editor. Versions come from
references/supported-dependencies.md (single source of truth:
scripts/lib/supported-dependencies.js).
node "${PLUGIN_ROOT}/scripts/generate-page-manifest.js" <working-dir> <kebab-slug>
<kebab-slug> is the same slug used for the working directory.--features charts,datepicker,timepicker (comma-separated) only whenthe requirements clearly call for them; otherwise omit and keep the
manifest lean.
--force to overwrite (used in regeneration flows when versions drift).
not block the workflow if the script returns non-zero — the manifest is a
dev-ergonomics aid, not part of the deployed artifact.
> **⚠️ CRITICAL — you MUST invoke genpage-planner via the Task tool. You MUST
> NOT inline the planner's questions yourself with AskUserQuestion.**
>
> The planner is not optional or skippable. It runs:
> 1. Prerequisite validation (node --version, pac help version >= 2.7.0)
> 2. Auth verification (pac auth list, environment selection)
> 3. The structured "Create new / Edit existing" question (via AskUserQuestion
> inside the planner subagent, not here)
> 4. Language detection (pac model list-languages) — only on new-page path
> 5. Entity existence detection (pac model list-tables --search)
> 6. App detection (pac model list) with proper selection prompts
> 7. Plan-mode presentation and approval
> 8. Writes genpage-plan.md to the working directory
>
> Reasons to NEVER ask "new or edit?" yourself before invoking the planner:
> - You would skip prereq + auth (the planner is the only thing that runs them)
> - The structured question gives the user labeled options; an inline free-text
> prompt forces them to guess
> - The planner returns { "action": "edit" } as a contract — your inline
> question can't produce that signal cleanly
>
> Even if $ARGUMENTS looks like it tells you the intent, **still invoke the
> planner**. Pass the intent in the prompt — the planner uses it to skip its
> own Question 1 if appropriate, but the prereq/auth/env steps still run.
genpage-planner via Task with the prompt below. Connector discoveryhas not run yet, so tell the planner the contract is the literal
No connector bindings. and that discovery is available on request (see 1a).
{ "action": "connector_discovery_required" }, invokegenpage-connector-builder with the returned intent — Mode: create for a
new page, Mode: edit if the planner also reported an edit — using the
environment URL the planner resolved, then invoke genpage-planner again with
the builder's ## Connector Bindings contract and connectors.json status.
{ "action": "edit" }, jump to the Edit Flow section.genpage-plan.md. Proceed to Phase 2.genpage-connector-builder is dispatched only by this top-level orchestrator,
not by genpage-planner. This keeps the connectors feature gate in one agent
while avoiding nested Task calls from the planner.
Never run discovery before the planner returns — not even when $ARGUMENTS
obviously mentions SharePoint, Teams, Office 365 or a custom REST source.
Discovery is a mutating operation: it can create a connection reference. The
planner is what resolves (a) create vs. edit and (b) which environment, and it may
resolve either differently from the active pac auth profile. A connection
reference created in the wrong environment, or in create mode for what turns out
to be an edit, cannot be undone by discarding the local outputs.
So the sequence is always: plan first, then discover, then re-plan.
No connector bindings.and is told discovery has not run.
$ARGUMENTSor from its own user clarification — it returns
{ "action": "connector_discovery_required", "intent": "..." } **together with
the resolved action and environment URL**.
genpage-connector-builder with that mode, that environmentURL, the working directory and ${PLUGIN_ROOT}. Read
<working-dir>/connector-bindings.md and verify <working-dir>/connectors.json
is a bare JSON array, then re-run the planner with the refreshed contract.
The builder remains the single owner of the connectors flag: it probes first,
writes No connector bindings. + [] when the flag is OFF, and performs all
connection discovery only when the flag is ON.
Pass a prompt that includes:
$ARGUMENTS${PLUGIN_ROOT}<working-dir>/connector-bindings.md,or the literal No connector bindings. when discovery was not needed or the
feature gate was OFF
<working-dir>/connectors.json exists and isa bare JSON array, or no connectors.json; omit --connectors
Example:
> You are the genpage-planner agent. Plan generative page(s) for the following requirements:
>
> [paste $ARGUMENTS here verbatim, or "no arguments provided — gather from user"]
>
> Working directory: [absolute path from Phase 0]
> Plugin root: ${PLUGIN_ROOT}
>
> Connector discovery is orchestrator-owned. Do not invoke
> genpage-connector-builder from inside the planner. Use this as the entire
> ## Connector Bindings section of the plan:
>
> [paste connector-bindings.md body, or No connector bindings.]
>
> Connector upload file: [absolute path to connectors.json, or none — omit --connectors]
>
> If your clarification questions reveal connector-backed data that is not covered
> by the connector contract above, stop and return
> `{ "action": "connector_discovery_required", "intent": "<connector need>",
> "resolvedAction": "create" | "edit", "envUrl": "<the environment you resolved>" }`
> instead of trying to discover connectors yourself. resolvedAction and envUrl
> are required — discovery is dispatched against exactly those.
>
> Follow the instructions in your agent file. Validate prereqs, confirm auth, ask
> the new/edit question via AskUserQuestion, then proceed accordingly. Write
> genpage-plan.md to the working directory if creating. Return the page list,
> entity status, app selection, and any { "action": "edit" } signal when complete.
Read genpage-plan.md from the working directory. Check the Entity Creation Required
section.
If the section literally says "No entity creation required — all entities already exist":
Skip to Phase 3.
If entities need creating:
Entity creation runs through the plugin's Node.js Web API scripts using az for
auth, and the az and pac identities should normally match. Run the
consolidated pre-flight:
node "${PLUGIN_ROOT}/scripts/check-auth.js" --require-pac
Genpage deploys pages via pac model genpage, so pass --require-pac to keep a missing pac login
a hard blocker (the app-builder skill omits the flag — its build path only needs the az token).
It returns a single JSON object:
{
"ok": true | false,
"blocker": null | "az_missing" | "az_not_logged_in" | "pac_not_logged_in"
| "no_env_url" | "whoami_403" | "whoami_401" | "whoami_error",
"message": "human-readable next step",
"warnings": ["..."],
"azUser": "...", "pacUser": "...", "envUrl": "...",
"identitiesMatch": true | false,
"whoAmI": { "ok": true, "userId": "...", "organizationId": "..." }
}
ok: true and identitiesMatch: true → proceed to 2b.ok: true and identitiesMatch: false → proceed to 2b but surface themessage to the user as an inline warning ("az is X, pac is Y — WhoAmI works
for now, but if entity creation later returns 403, run the suggested
az login --username to align them").
ok: false → show the message field to the user verbatim andstop the workflow. The script already includes a fix-it command for every
blocker (run az login, etc.).
Capture envUrl from the result — Phase 2b passes it to the entity-builder.
Invoke the genpage-entity-builder agent via the Task tool. Pass in the prompt:
genpage-plan.md${PLUGIN_ROOT}pac org who)The entity-builder reads Solution and Publisher Prefix directly from the
plan's ## Environment — no need to re-thread them here.
Wait for completion. The builder writes a transactional log at
<working-dir>/entity-creation-log.md for recovery on failure.
Read genpage-plan.md for the app decision and the Solution line in
## Environment.
If "create new":
pac model create --name "App Name" --solution "<Solution unique name>" --publish
--solution is mandatory. pac model create errors out with
"The given solution name is not valid: ()" if you omit it — its claimed
"active solution" fallback does not work in practice.
--publish is mandatory. Without it the new appmodule stays in draft and
the genux runtime URL errors with "app not published".
Solution value verbatim. The planner always writes one(default fallback is literally Default).
Solution, pass --solution Default —every Dataverse env has a built-in "Default Solution" by that unique name.
Store the new app-id for Phase 6.
If existing app-id: Use it directly. pac model create is not called, so
the Solution line is informational only for this phase.
If any page uses Dataverse entities, generate the TypeScript schema:
pac model genpage generate-types --data-sources "entity1,entity2,..." --output-file <working-dir>/RuntimeTypes.ts
> Windows + Bash: Always use forward slashes in file paths (e.g., D:/temp/RuntimeTypes.ts).
After generating, read the RuntimeTypes.ts file to verify it generated correctly.
For mock data pages only: Skip this phase.
Re-probe the feature gate here — do not rely on the plan content alone. A plan
authored while the flag was ON must not deploy connectors after it is turned OFF:
node "${PLUGIN_ROOT}/scripts/lib/feature-flags.js" connectors
If it prints disabled: connectors are OFF. Skip this phase entirely — do not
create or pass connectors.json, and never add --connectors on upload —
regardless of what the plan's ## Connector Bindings section says. (Backstop:
list-connections.js / create-connection-reference.js also fail closed with
exit 3 if invoked while OFF.)
Carry this decision into code generation. The probe result is `Connectors:
disabled / Connectors: enabled` for the rest of the run, and Phase 5 must pass
it verbatim in every page-builder dispatch. When it is disabled, every downstream
step treats the plan's ## Connector Bindings section as if it read
No connector bindings. — otherwise the generated page would call a connector that
this run deliberately never binds, and the page fails at runtime instead of simply
omitting the feature.
If it prints enabled: read the plan's ## Connector Bindings section and
treat it as bindings only when it contains an actual binding table (a
| Logical Name | … header with at least one data row). If the section is
No connector bindings., empty, missing, or malformed, treat the page as having
no connectors and skip this phase.
When there are real bindings, the genpage-connector-builder agent already wrote
<working-dir>/connectors.json during planning — verify it exists and matches the
plan table. If it is missing, derive it from the plan table as a **bare JSON
array** (never the { "connectorBindings": [...] } object wrapper — that is the
deployed page config.json shape that pac writes):
[
{
"logicalName": "new_uxtest_sharepoint",
"connectorId": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline",
"dataset": "https://host.sharepoint.com/sites/x",
"tables": ["5709dd6f-c73e-4079-ad23-2334e45e0e13"],
"tableDisplayNames": ["Pet"]
},
{
"logicalName": "new_uxtest_msnweather",
"connectorId": "/providers/Microsoft.PowerApps/apis/shared_msnweather",
"dataset": "",
"operations": ["CurrentWeather"]
}
]
Do not write connection IDs into connectors.json — the importing maker/admin
fills env-specific ConnectionId values through solution deployment settings.
Read genpage-plan.md and extract the pages table.
Before invoking any builders, verify:
## Pages table### [Page Name] subsection in ## Per-Page Specifications## Pages table are unique. If any are duplicated,rewrite the plan appending -1, -2, etc. before dispatch. Duplicate filenames
cause silent last-writer-wins data loss under parallel execution.
See ${PLUGIN_ROOT}/references/plan-schema.md for the full contract.
If the plan's Pages table contains exactly one row, do NOT dispatch a Task
subagent. Inline the page-builder workflow directly in the orchestrator:
${PLUGIN_ROOT}/references/rules.md## Relevant Samplesenabled and the plan's## Connector Bindings section contains an actual binding table (a
| Logical Name | … header with at least one data row), also read
${PLUGIN_ROOT}/references/connectors.md. Treat a No connector bindings.
sentinel, an empty/missing/malformed section, or a disabled probe as
having no connectors (same contract as Phase 4.5 and genpage-page-builder).
Needs caching: true, also read${PLUGIN_ROOT}/references/data-caching.md
## Environment indicates non-English languages, also read${PLUGIN_ROOT}/references/localization.md
genpage-plan.md (already in working directory) and RuntimeTypes.tsif Data mode is dataverse
.tsx file to <working-dir>/<filename>.tsx following all rules@fluentui/react-icons against${PLUGIN_ROOT}/references/verified-icons.txt (one Grep per name).
Rewrite any unverified names with the closest verified alternative; do not
load the full icon list into context
This saves ~5-15s of Task overhead and ~3K tokens that would otherwise be
duplicated in a subagent context.
If the plan's Pages table contains 2+ rows, invoke a genpage-page-builder
agent via the Task tool per page. Fire all invocations in a single message
for parallel execution.
For each page, pass a prompt that includes:
genpage-plan.mdenabled or disabled — the Phase 4.5 probe result, verbatim${PLUGIN_ROOT}For Dataverse pages, include the RuntimeTypes line:
> You are the genpage-page-builder agent. Generate the [Page Name] page.
>
> - Target file: [filename].tsx
> - Plan document: [absolute path to genpage-plan.md]
> - Data mode: dataverse
> - Connectors: [enabled|disabled from Phase 4.5]
> - RuntimeTypes: [absolute path to RuntimeTypes.ts]
> - Working directory: [absolute path from Phase 0]
> - Plugin root: ${PLUGIN_ROOT}
>
> Follow the instructions in your agent file. Write [filename].tsx and return your
> result when done.
For mock data pages, omit the RuntimeTypes line and set Data mode: mock:
> You are the genpage-page-builder agent. Generate the [Page Name] page.
>
> - Target file: [filename].tsx
> - Plan document: [absolute path to genpage-plan.md]
> - Data mode: mock
> - Connectors: [enabled|disabled from Phase 4.5]
> - Working directory: [absolute path from Phase 0]
> - Plugin root: ${PLUGIN_ROOT}
>
> Follow the instructions in your agent file. Write [filename].tsx and return your
> result when done.
Wait for all page-builder tasks to complete before proceeding.
For each .tsx file produced, deploy to Power Apps.
If Phase 4.5 wrote <working-dir>/connectors.json, first pre-flight the active
PAC CLI:
pac model genpage upload --help
The help output must contain --connectors. If it does not, stop and surface:
"connector deploy requires a pac build with `pac model genpage upload
--connectors` — build from PowerPlatform-Scale-AdminTools or update pac." Do
not silently drop bindings.
Connector deployment matrix:
--connectors "<working-dir>/connectors.json"with the first upload --add-to-sitemap.
binding set to connectors.json and include --connectors with
upload --page-id <id> (full replace).
--connectors; pac preserves existingbindings. Never pass a stale or empty file on an unrelated edit.
[] to connectors.json and pass--connectors so pac clears the page's connectorBindings.
Copy the upload commands below exactly — --app-id, --code-file, --prompt, --agent-message are all required and must use these exact flag names.
Log the full command verbatim into workflow-log.md under a ## Phase 6 — Deploy section before invoking it. Including --prompt and all other flags. The eval harness greps the log for these tokens — a terse summary like Command: pac model genpage upload --add-to-sitemap will fail the --prompt scoping assertion. Format:
## Phase 6 — Deploy
- Command: `pac model genpage upload --app-id <id> --code-file <path> --data-sources '<entities>' --prompt "<full prompt>" --model <model-id> --name "<page name>" --agent-message "<description>" --add-to-sitemap`
- Result: page-id = <returned-id>, status = success
When connector bindings are present, the logged command must also include
--connectors "<working-dir>/connectors.json".
--prompt semantics--add-to-sitemap, no --page-id): full page descriptionfrom plan's ## User Requirements.
--page-id, no --add-to-sitemap): delta only —the changes in this upload, written like a commit message, never a
re-statement of the original.
Applies in Phase 6 updates, Phase 6.5 PAGEREF re-uploads, Phase 7.5 fix
re-deploys, and the entire edit flow.
pac model genpage upload `
--app-id <app-id> `
--code-file <working-dir>/<file>.tsx `
--name "Page Display Name" `
--data-sources "entity1,entity2" `
--connectors "<working-dir>/connectors.json" `
--prompt "<Full page description from plan's ## User Requirements>" `
--model "<current-model-id>" `
--agent-message "Description of what was built and any relevant details" `
--add-to-sitemap
Omit the --connectors line when Phase 4.5 did not write connectors.json.
For mock data pages: Same but omit --data-sources.
Use --page-id, omit --add-to-sitemap, and scope --prompt to the delta only:
pac model genpage upload `
--app-id <app-id> `
--page-id <page-id> `
--code-file <working-dir>/<file>.tsx `
--data-sources "entity1,entity2" `
--connectors "<working-dir>/connectors.json" `
--prompt "<Only the changes in this upload, e.g. 'Add a search box and sort by company name'>" `
--model "<current-model-id>" `
--agent-message "Description of what was changed in this upload"
For updates, include the --connectors line only when this upload intentionally
replaces or clears connector bindings; otherwise omit it to preserve the
deployed page's current bindings.
Runs only when the plan has 2+ pages AND any built .tsx contains a PAGEREF_
token. Page-builders emit pageId: "PAGEREF_<filename-without-tsx>" as a
placeholder because GUIDs don't exist until after Phase 6 (see Rule 13). This
phase substitutes the real GUIDs.
filename-without-tsx → page-id map from Phase 6 upload output.PAGEREF_pet can't match insidePAGEREF_pet-gallery.
.tsx in <working-dir>/*.tsx (top level only, no recursion),replace every quoted "PAGEREF_<name>" (must be in double quotes — that's
the format page-builders emit) with "<page-id-guid>".
and report — never silently ship the literal string.
of pac model genpage upload (--page-id, no --add-to-sitemap). Per the
"--prompt semantics" rule in Phase 6, this is an update, so --prompt
describes the delta only — not the original page description:
pac model genpage upload `
--app-id <app-id> `
--page-id <page-id-from-Phase-6> `
--code-file <working-dir>/<file>.tsx `
--data-sources "entity1,entity2" `
--prompt "Resolve cross-page navigation placeholders to real page GUIDs (post-deploy fix-up)" `
--model "<current-model-id>" `
--agent-message "Replaced PAGEREF_<name> tokens with actual page IDs returned by Phase 6"
Pages with no PAGEREF_ strings need no second upload.
Runs only when the plan's ## Solution Packaging has Package into solution: true.
Adds the deployed app, the GenPage(s), and any connection references to the
target solution so they travel cross-environment.
node ${PLUGIN_ROOT}/scripts/provision-solution.js <envUrl> <solutionUniqueName> "<Friendly Name>" [--publisher <uniqueName>]
It prints { "ok": true, "solutionId": …, "uniqueName": …, "publisherPrefix": … };
uniqueName must start with a letter and contain only letters, digits, and
underscores. Without --publisher it resolves the environment's default publisher.
by Phase 6 as --page-ids — the GenPage is added explicitly, it does NOT travel
with the app on its own):
node ${PLUGIN_ROOT}/scripts/add-page-to-solution.js <envUrl> <solutionUniqueName> <app-id> --page-ids "<page-id1,page-id2>" --connection-refs "<logicalName1,logicalName2>"
workflow-log.md.Cross-env note (verified 2026-07-10): the app (80) pulls the sitemap (62); the
GenPage uxagentproject (10372) is added explicitly and pulls its
uxagentprojectfile rows (10373, incl. config.json with connectorBindings);
each connectionreference (10158) is added so bindings resolve. At import the
deployer supplies env-specific ConnectionId per connection reference via
pac solution create-settings + pac solution import --settings-file.
After successful deployment, ask the user via AskUserQuestion:
> "Would you like to verify the page(s) in the browser using Playwright?"
Options: Yes, verify in browser / Skip verification
${PLUGIN_ROOT}/skills/genpage/verify-flow.mdfor the full Playwright verification workflow (navigate, structural
verification including below-the-fold, interactive testing, screenshots,
fix-and-redeploy). The orchestrator only loads that file on demand to keep
context lean when verification is skipped.
By Phase 8 the workflow-log.md should already contain Phase 0 through Phase 7
sections written incrementally — the planner writes Phase 1 inside its agent
context, you (the orchestrator) write Phase 0 / 0.5 / 3 / 4 / 6 / 6.5 / 7 as
each runs, and the entity-builder and page-builder agents append their own
Phase 2 / 5 sections when invoked.
In Phase 8, append a final ## Phase 8 — Summary section to the same file:
## Phase 8 — Summary
| Page | File | Entities | Status |
|------|------|----------|--------|
| <Name> | <file>.tsx | <entities or "mock data"> | Deployed |
- App: <name> (<app-id>)
- Entities created: <list, or "none">
- Browser verification: <skipped | confirmed | failed: <reason>>
The log MUST contain command-level entries for every prereq / auth / question /
upload / script invocation — not just outcome summaries. The eval harness greps
the log for tokens like node --version, pac auth list, AskUserQuestion,
EnterPlanMode, --prompt, check-auth.js, etc. A decision-only log
(e.g., Decision: new page without the underlying AskUserQuestion) will
fail Layer 1 assertions even when the agent's behavior was correct.
Then present a final summary to the user:
## Genpage Complete
| Page | File | Entities | Status |
|------|------|----------|--------|
| [Name] | [file].tsx | [entities or "mock data"] | Deployed |
App: [app name] ([app-id])
Screenshots: [if verification was done]
Next steps: Share with team, iterate on design, create additional pages
For the edit flow (triggered when the genpage-planner returns
{ "action": "edit" }), see edit-flow.md in this folder.
The edit flow has its own 8 phases (Edit Phase 1-8): discover and select target
app + page via pac model list + pac model genpage list, download, generate
RuntimeTypes if needed, invoke genpage-edit-planner, apply the edit inline,
deploy, verify, summarize.
Take microsoft/genpage from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npm.
Without those the skill loads but fails at the first command.