Skip to content
cccskills
Official · MicrosoftFreePublished on GitHub

edit-offline-profile

Internal mobile-app workflow read and executed only by mobile orchestrators to change one table's scope, columns, or sync settings in a Mobile Offline Profile.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md13.4 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Shared instructions: shared-instructions.md — read first.

References:

Edit Offline Profile

Re-run a single piece of an existing profile — change one table's row scope, update the column list, adjust sync frequency, or rename the profile. Avoids the cognitive cost of walking the full /setup-offline-profile wizard for a one-line change.

Scope: existing profile (read from <working_dir>/offline-profile.json or --profile-id); edits at the table-item granularity. To ADD a new table return to the owner for /add-table-to-offline-profile; deleting an entire profile is outside this helper's scope, not an automatic recovery.

Invocation context and root binding

This internal helper is always a child invocation. Before any project read or command, execute app-working-directory.md. Require the owner's absolute working_dir; --working-dir, when supplied, must identify that same native-canonical root. Missing, relative, or conflicting root context returns NEEDS_CONTEXT before project access; an inaccessible directory is BLOCKED. Never use the launch cwd or introduce another root resolver.

Receive MOBILE_APP_ORCHESTRATING=1, orchestrator, working_dir, phase, approved_scope, and supplied answers from the owner. Preserve the selected environment ID/URL/tenant, profile ID, table allowlist, item IDs, and exact scope/column/sync/name/description changes. Missing or conflicting scope returns NEEDS_CONTEXT, not another environment/profile/table selection. The marker is not approval: schema approval alone does not authorize profile edits, publication, deletion, or membership edits. Retain Step 3's single confirm unless supplied answers already approve that same current offline change. For --plan-only or a planning-phase handoff, return the current/proposed diff after read-only discovery and STOP before Apply, PATCH, publication, or artifact writes. A gate response cannot lift proposal-only mode.

Apply the canonical contract to every shell call and file tool, including version/auth checks, manifest/screen reads, commands copied from references, verification, recovery, and retries. Re-supply the absolute PLUGIN_ROOT and required context on each call; no prior cd, export, or shell variable persists. Use the canonical Bash guard below or its PowerShell equivalent. Replace placeholders with validated, shell-quoted invocation values; missing values return NEEDS_CONTEXT, never execute an unresolved placeholder. File tools use absolute <working_dir>/... paths; plugin reads/scripts use ${PLUGIN_ROOT}/.... Never search another app when a file is absent.

Workflow

  1. Verify project + locate profile → 2. Resolve target (which table / what to change) → 3. Show current vs proposed → Single confirm → 4. PATCH → 5. Publish → 6. Update artifacts → 7. Summary

Step 1 — Verify project + locate profile

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

Read <working_dir>/power.config.json and require a nonempty environmentId matching the owner's selected environment. Reuse matching complete environment context, or perform this non-persisting lookup from the bound root:

cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
environment_id=$(node -p "require('./power.config.json').environmentId || ''") || {
  echo "BLOCKED: unreadable power.config.json" >&2; exit 1;
}
if [ -z "$environment_id" ] || [ "$environment_id" != "<selected-environment-id>" ]; then
  echo "NEEDS_CONTEXT: selected environment does not match working_dir" >&2
  exit 1
fi
node "${PLUGIN_ROOT}/scripts/resolve-environment.js" "$environment_id" --no-cache --require-tenant

Require the returned environment ID, HTTPS URL, and tenant to match the owner; incomplete or conflicting context returns NEEDS_CONTEXT. Never drop --no-cache --require-tenant, change configuration, or switch auth/environment to recover a failure. Use that exact <envUrl> and <tenantId> for every request.

Profile ID resolution (same priority as /assign-offline-profile Step 1):

SourceUsed when
$ARGUMENTS --profile-id <guid>Explicit selection matching the owner's profile context
<working_dir>/offline-profile.json top-level profileIdDefault for /setup-offline-profile-created projects, matching the owner's profile context
OtherwiseReturn NEEDS_CONTEXT to the owner to select the intended profile; never pick another profile silently

STOP if no profile found: "Run /setup-offline-profile first."

Verify the selected live profile belongs to the selected environment. A conflicting snapshot/argument returns NEEDS_CONTEXT; a 404 or permission failure is BLOCKED, not permission to discover a substitute. Resolve each <itemId> only within this profile and verify its table matches the exact requested logical name.

<working_dir>/power.config.json is intentionally NOT consulted for the profile ID. That file is owned by pa app init. The local profile ID lives in <working_dir>/offline-profile.json only.

Step 2 — Resolve target (what to edit)

Parse $ARGUMENTS:

Flag patternEffect
--rename <new-name>Update profile name
--describe <text>Update profile description
--table <logical-name> --scope <0|1|2>Change one table's recorddistributioncriteria. Combine with --me, --team, --bu to set sub-flags when scope=2.
--table <logical-name> --sync <minutes>Change one table's syncintervalinminutes (range 5–1440)
--table <logical-name> --columns add:col1,col2 remove:col3Add or remove logical names from selectedcolumns. Comma-separated, both add/remove optional.
--table <logical-name> --columns resetReplace selectedcolumns with union of always-include + manifest lookups + screen-grep'd (re-runs the architect's Step 6 union for this table)

If no flags → interactive picker. AskUserQuestion with up-to-4 most likely edits:

  • "Rename profile"
  • "Change a table's scope" → next message asks which table
  • "Change a table's sync frequency" → next message asks which table + value
  • "Edit a table's column list" → next message asks which table + add/remove

Restrict the picker and explicit flags to the owner's exact current scope. --columns reset reads the selected table from <working_dir>/.datamodel-manifest.json or <working_dir>/docs/plan-artifacts/.datamodel-manifest.json and greps only absolute <working_dir>/app/ and <working_dir>/src/ paths. Keep the chosen absolute manifest path for the whole invocation; no other-app fallback. Changing the table, profile, or operation requires returning NEEDS_CONTEXT to the owner, not expanding an existing approval.

Step 3 — Show current vs proposed (single gate)

GET the current item state from Dataverse for any tables being edited:

cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
node "${PLUGIN_ROOT}/scripts/dataverse-request.js" "<envUrl>" GET \
  "mobileofflineprofileitems(<itemId>)?\$select=name,recorddistributioncriteria,recordsownedbyme,recordsownedbymyteam,recordsownedbymybusinessunit,syncintervalinminutes,selectedcolumns" \
  --tenant-id "<tenantId>"

Render a current/proposed diff:

Profile  : <name> (<id>)
Editing  : <table-logical-name> item

                Current     →    Proposed
Scope         : Org+me     →    All records
Sync (min)    : 10         →    30
Columns       : 14         →    16  (add: chnl_notes, chnl_actual_visit_date)

AskUserQuestion: "Apply this change? [Apply / Cancel]"

If Apply → continue. If Cancel → STOP.

Step 4 — PATCH

Build the PATCH body with only the fields that changed:

cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
node "${PLUGIN_ROOT}/scripts/dataverse-request.js" "<envUrl>" PATCH \
  "mobileofflineprofileitems(<itemId>)" --tenant-id "<tenantId>" \
  --body '{
    "recorddistributioncriteria": <new>,
    "recordsownedbyme": <new-bool>,
    "syncintervalinminutes": <new>,
    "selectedcolumns": "{\"Columns\":[...]}"
  }'

For profile-level edits (name / description):

cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
node "${PLUGIN_ROOT}/scripts/dataverse-request.js" "<envUrl>" PATCH \
  "mobileofflineprofiles(<profileId>)" --tenant-id "<tenantId>" \
  --body '{"name": "...", "description": "..."}'

Check returned HTTP statuses as well as process exit. A 403/PrivilegeCheckFailed is BLOCKED; do not change identity or profile to bypass permissions. Preserve every field and table outside the approved delta.

Step 5 — Publish

Use the targeted PublishXml recipe from shared/references/dataverse-offline-api.md §9:

cd -- '<working_dir>' || { echo "BLOCKED: cannot enter working_dir" >&2; exit 1; }
node "${PLUGIN_ROOT}/scripts/dataverse-request.js" "<envUrl>" POST \
  "PublishXml" --tenant-id "<tenantId>" --body '{
    "ParameterXml": "<publish><mobileofflineprofiles><mobileofflineprofile><profileId></mobileofflineprofile></mobileofflineprofiles></publish>"
  }'

Publishes only this profile, not the entire org's customizations. Avoids the 429 rate-limit storms on shared envs that the legacy PublishAllXml triggered.

On 400 / 0x80071141 "circular relationship" — same handling as /setup-offline-profile Step 8 (parse cycle path, prompt user to drop one association, retry). Fallback to PublishAllXml only if PublishXml returns an unexpected error other than the cycle case and the owner/user approves that broader publication; a profile-scoped approval does not cover it.

All linked recovery commands, re-GET verification, and retries must bind the same root and pass the same environment/tenant/profile/table/item context. Never retry on a different profile or bypass a permission failure. Confirm publication before Step 6.

If a linked recipe requires another offline helper, return the proposed change to the owner for approval first, then read the helper at its absolute ${PLUGIN_ROOT}/skills/<helper>/SKILL.md path. Forward:

Arguments: --working-dir '<working_dir>' --profile-id "<profileId>" --table "<table>" <exact-approved-change-flags>
Context: MOBILE_APP_ORCHESTRATING=1, orchestrator, working_dir, phase, approved_scope,
         selected environment ID/URL/tenant, profile ID, table allowlist, item ID,
         supplied answers, --plan-only (if present)

The recipient must enforce the same canonical root contract. Missing context returns NEEDS_CONTEXT; do not fall back to direct invocation or rediscovery.

Step 6 — Update artifacts

Re-read the changed item(s) in the same profile/environment and rewrite the matching entry in <working_dir>/offline-profile.json. When the edit changed a table's columns (--columns add:/remove:/reset), also refresh that entry's schemaColumns to the table's current full column set from the bound absolute manifest. This re-baselines the schema-reconciliation marker for the next ${PLUGIN_ROOT}/scripts/offline-profile-delta.js run; leave schemaColumns untouched for scope/sync/rename-only edits. Any delta check uses --project-root '<working_dir>' in its own guarded call. See offline-profile-reconciliation.md.

Append a one-line entry to <working_dir>/memory-bank.md ## Offline profile block:

edits:
  - { at: 2026-05-19T..., field: 'chnl_storevisit.syncintervalinminutes', from: 5, to: 10 }

Step 7 — Summary

✓ Edited profile.

  Field : chnl_storevisit.syncintervalinminutes
  From  : 5 min
  To    : 10 min
  Published: 2026-05-19T...

offline-profile.json + memory-bank.md updated.

Status code (final line)

  • DONE — change applied and published
  • DONE_WITH_CONCERNS: <list> — change applied but with caveats (publish timeout-then-success, etc.)
  • NEEDS_CONTEXT: <missing> — couldn't resolve target table or profile
  • BLOCKED: <reason> — auth or PATCH failure

What this skill does NOT do

  • Add a NEW table to the profile → use /add-table-to-offline-profile
  • Add a new association (relationship inclusion) → blocked on v0.2 selectedrelationshipsschema work; use maker portal in the meantime
  • Delete the whole profile → manual DELETE /mobileofflineprofiles(<id>) (cascade-deletes items + associations); see dataverse-offline-api.md §11
  • Migrate the profile between environments → use CloneMobileOfflineProfile action (v0.5 work)

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

activate-site

Official · MicrosoftFree

Activates and provisions a Power Pages website in a Power Platform environment via the Power Platform REST API. Use when the user wants to activate, provision, turn on, or enable a Power Pages website or portal.

The English summary is being prepared. Showing the original description.

microsoft/power-platform-skills1KUpdated Oct 20, 2026

add-ai-webapi

Official · MicrosoftFree

Integrates Power Pages generative-AI summarization APIs (PREVIEW) into a Single Page Application (SPA) site — the Search Summary API and the Data Summarization API — on any record-detail or list page. Generates per-target service code (CSRF-handled) and AI site settings; delegates Web API settings, table permissions, and web roles to `/integrate-webapi` and `/create-webroles`. Use whenever a user wants AI/Copilot output that condenses Dataverse content on a Power Pages site — an AI summary, AI-generated overview or "key insights" across a record or list, a search-results summary, a case/incident summary, or recommendation-chip refinement — even when phrased as "AI-generated paragraph", "insights", or "overview". Do NOT use for: generative pages in model-driven apps (use the model-apps `genpage` skill), Copilot Studio agents/chatbots, summarizing documents or PDFs, Power BI dashboards, plain keyword search with no AI summary, or plain Dataverse CRUD (use `/integrate-webapi`).

The English summary is being prepared. Showing the original description.

microsoft/power-platform-skills1KUpdated Oct 20, 2026

add-azuredevops

Official · MicrosoftFree

Adds Azure DevOps connector to a Power Apps code app. Use when querying work items, creating bugs, managing pipelines, or making ADO API calls.

The English summary is being prepared. Showing the original description.

microsoft/power-platform-skills1KUpdated Oct 20, 2026

add-camera

Official · MicrosoftFree

Internal implementation skill invoked by /add-native for camera, image picker, barcode scanner, QR scanner, and camera/gallery Dataverse artifact workflows.

The English summary is being prepared. Showing the original description.

microsoft/power-platform-skills1KUpdated Oct 20, 2026

add-cloud-flow

Official · MicrosoftFree

Integrates Power Automate cloud flows into a Power Pages site. Lists available flows, suggests relevant ones based on intent, identifies scenarios and web roles, creates metadata files, and generates client-side code to call flows. Handles both new flow registration and adding already-registered flows to additional pages without re-creating metadata. Use when the user wants to add, connect, register, or link a Power Automate cloud flow to their site.

The English summary is being prepared. Showing the original description.

microsoft/power-platform-skills1KUpdated Oct 20, 2026

add-connector

Official · MicrosoftFree

Use when adding a Power Platform connector to an Expo/React Native Power Apps mobile app and no dedicated mobile connector skill exists.

The English summary is being prepared. Showing the original description.

microsoft/power-platform-skills1KUpdated Oct 20, 2026

See all skills by microsoft

Report a problem with this skill