You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: plugins/mobile-apps/agents/screen-builder.md
+13-3Lines changed: 13 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -32,7 +32,7 @@ You will be invoked by `/create-mobile-app` Step 11 or `/edit-app` screen-rebuil
32
32
-**Use shared code via path aliases — NEVER re-define inline.** The project has `@/components`, `@/hooks`, `@/utils`, `@/tokens` configured in tsconfig. Import from them:
-**Hooks:**`import { useListData, useCursorListData, useSearchFilter } from '@/hooks'` — use `useListData` only for bounded list screens whose spec says `pagination: none`. For unbounded Dataverse screens whose spec says `pagination: cursor`, use `useCursorListData`, `useInfiniteQuery`, or an app-specific cursor hook generated by the orchestrator. Use `useSearchFilter` only for bounded client-side lists; cursor lists must push search into the service call with `filter`.
-**Generated:**`import { FooService } from '@/generated/services/FooService'` and `import type { Foo } from '@/generated/models/FooModel'`
37
37
-**Native:**`import { captureFromCamera } from '@/native/camera'`
38
38
Do NOT define `function LoadingState()`, `function formatDate()`, `function Field()`, `function Section()`, or status color maps inside your screen. Do NOT write the `useState(loading) + useFocusEffect(load) + onRefresh` pattern manually — use `useListData` instead.
@@ -199,7 +199,11 @@ You will be invoked by `/create-mobile-app` Step 11 or `/edit-app` screen-rebuil
-**FormpickerUI** — whentheform's spec calls for a parent picker (e.g., "select Project"), the picker stores the selected record's`id` (GUIDstring), andthesubmithandlerconvertsittothebindstringatwritetime. Neverstorethebindstringincomponentstate — onlyintheAPIpayload.
205
209
-**Paginationrule:**Ifyourspecsays`pagination: cursor`, doNOTuse`useListData`or`useSearchFilter`. Usetheskeleton's `useCursorListData` call, React Query's`useInfiniteQuery`, oranapp-specific`use<Entity>CursorList`hookwithFlatList`onEndReached`perthepatternin [`data-performance.md`](${PLUGIN_ROOT}/shared/references/data-performance.md). Neverfetchallrecordsatonce, andnevertreat`top: 50`aspagination. RealgeneratedDataverseservicesuseSDK`maxPageSize`forpagesizeandreturn`IOperationResult.skipToken`forthenextpage; passthatvaluebackas`skipToken`. Alwaysincludedeterministic`orderBy`withauniquekeyand`select`intheservicecall. Pushsearch/filterintoDataversewith`filter`. Ifthegeneratedserviceintheappdoesnotexpose`maxPageSize`/`skipToken`foranunboundedtable, return`BLOCKED [<screen_name>]: generated service does not expose cursor paging for <Service>; do not downgrade to useListData`.
@@ -948,7 +952,13 @@ Before finishing the screen, mentally verify:
26. **Create / updatepayloadsNEVERincludeserver-managedcolumns.** Forbiddenkeysinany `*Service.create({...})` or `*Service.update({...})` object literal: `ownerid`, `owneridtype`, `statecode`, `statuscode`, `importsequencenumber`, `overriddencreatedon`, `timezoneruleversionnumber`, `utcconversiontimezonecode`, `versionnumber`, `createdon`, `modifiedon`, `createdby`, `modifiedby`. If the generated model type marks them required, put the unavoidable cast inside the narrow write helper — never emit junk like `ownerid: ''` or `statecode: 0` to satisfy the type. Bug killed: every HTTP 400 on save.
Copy file name to clipboardExpand all lines: plugins/mobile-apps/agents/screen-planner.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -231,7 +231,7 @@ Examples:
231
231
-`inspections` (list + detail + form, no detail children) → folder `app/(app)/inspections/` with `index.tsx`, `[id].tsx`, `new.tsx`
232
232
-`inspections` (detail owns photo/edit children) → `app/(app)/inspections/[id]/index.tsx`, `app/(app)/inspections/[id]/photo.tsx`, and `app/(app)/inspections/[id]/edit.tsx`; never also create `inspections/[id].tsx`
233
233
234
-
**Authenticated Dataverse identity rule:** when app identity links through `systemuser`, require `SystemusersService` in the data-source/service plan. Resolve the access-token `oid` with `SystemusersService.getAll({ filter: "azureactivedirectoryobjectid eq <oid> and isdisabled eq false", top: 2 })`, then query the profile table with `_lookup_value eq <systemuserid>`. Do not use relationship traversal (`lookupNavigation/azureactivedirectoryobjectid`) in generated mobile-service filters.
234
+
**Authenticated Dataverse identity rule:** when app identity links through `systemuser`, require `SystemusersService` in the data-source/service plan and record the profile table's planned `systemuser` lookup logical column. Resolve the access-token `oid` with `SystemusersService.getAll({ filter: "azureactivedirectoryobjectid eq <oid> and isdisabled eq false", top: 2 })`. The screen-builder must open the generated profile model and use the exact declared read property corresponding to that lookup column (for example `_new_systemuserid_value` for `new_systemuserid`). Never put generic placeholders such as `_lookup_value` or `_systemuserlookup_value` in a concrete filter, and do not use relationship traversal (`lookupNavigation/azureactivedirectoryobjectid`).
235
235
236
236
Keep total screen count tight — under 8 for v0 unless the requirements explicitly demand more. The user can iterate later.
Raw Dataverse Web API examples often use a case-sensitive navigation-property schema name. Generated Power Apps services instead expose their accepted write key in `src/generated/models/<Entity>Model.ts`. For generated services, that model declaration is authoritative; never convert it to PascalCase or hide an unverified key inside `Record<string, unknown>`.
258
258
259
-
For filtering across lookups, avoid `lookupNavigation/relatedColumn eq ...` in generated mobile-service options. Resolve the related row through its generated service, then filter with the source record's `_lookuplogicalname_value` GUID property.
259
+
For filtering across lookups, avoid `lookupNavigation/relatedColumn eq ...` in generated mobile-service options. Resolve the related row through its generated service, then open the source generated model and copy the exact declared read lookup property corresponding to the planned lookup logical column (for example `_new_systemuserid_value` for `new_systemuserid`). Never substitute generic placeholders such as `_lookup_value` or `_systemuserlookup_value` into generated code.
260
+
261
+
Dynamic route IDs used in Dataverse calls must be normalized with the shared `normalizeDataverseGuid` helper from `@/utils`. Do not generate an inline RFC UUID regex: Dataverse sequential GUIDs may not contain RFC version bits even though their hexadecimal `8-4-4-4-12` structure is valid.
260
262
261
263
The `@odata.bind` value must be an entity set path with the GUID: `/<entitysetname>(<guid>)`
0 commit comments