Mobile Fleet - Knees of Ants: the phone and tablet side of the fleet. FleetAgent, a Power Apps canvas app, the Power Automate flows that carry records between each operator's laptop and five SharePoint lists, and the workbook that creates those lists. Several operators share one site; each sees their own fleet, and the team's read-only.
Imported from this-next-please@913d157765ef8a7944d21a8167b15771edfd133f (mobile/ and
tests/test_mobile_powerapp.py, history kept; this-next-please#600). The laptop side, the bridge
(ad-fleet mobile ...), stays in this-next-please; the two meet only at the contract pinned in
contract/. Nothing here imports the laptop's Python package.
Koa also holds the Data Czars SharePoint site (site/), the home of data-czars and the tooling it supports in
the PAE: its design, its lists and their formatting, a sheet per page with the Copilot prompt that builds it, the
intake to Jira, the usage reports' landing, the site's Copilot skill and its three agents, and the prompts that scan
data-czars, usage_tool and the fleet for the facts the pages need (they stay on your machine). Start at
site/README.md.
To run the first round trip (a prompt typed on the phone reaching an agent on the laptop), follow TESTING.md.
The laptop's fleet bridge (ad-fleet mobile init, then the bridge loop) writes an outbox of small
JSON records into the operator's own folder of the site's FleetAgent library, synced to the laptop as a
OneDrive shortcut: one attention row per repository, a mirror of every approval request, every
notification, and a heartbeat. Power Automate flows (one copy per operator) move those records into five
SharePoint lists, each row stamped with its Operator, and send a push to that operator. FleetAgent, the canvas app in powerapp/, shows those rows on
a phone or a tablet and carries the operator's decision or reply back through one flow, which writes
a file into the sender's own bridge inbox. The laptop applies it only after checking the digest, the operator's
identity and the expiry. The app decides nothing: every state, colour and sentence on its screens is
the laptop's own word, and the only rule of its own is "three missed heartbeats means not syncing".
README.md this build sheet
build/ the Copilot build: README (the operator's part), steps/01-07 (the agent's), prepare.py
.github/skills/ build-fleetagent (runs build/) and build-czars-site (site/'s lists), skills a Copilot agent loads
.github/prompts/ czars-scan-*: read data-czars, usage_tool and the fleet into site/local/ (never committed)
site/ the Data Czars SharePoint site: design, lists, page sheets, intake, skill, agents (site/README.md)
TESTING.md the first round trips: a reply (a prompt) and an approval, phone to laptop
AGENTS.md for coding agents working in this repository
contract/
PIN the pinned tag, its source commit and the sha256 of every file below
fleet-mobile.v1.schema.json the laptop's records, the inbox records, the five lists, FleetDecide's signature
examples/*.json one record per kind, the Parse JSON samples
powerapp/
NOTES.md every decision taken where the research left something unverified
src/ the app, one entity per file, exactly as Studio lays sources out
App.pa.yaml StartScreen, BackEnabled, OnError, Formulas (typed by hand, step 6)
_EditorState.pa.yaml screen and component order
Screens/*.pa.yaml HomeScreen, AgentScreen, ApprovalScreen, DecideScreen, ReplyScreen, SettingsScreen
Components/*.pa.yaml EmptyState, KeyValueRow, FleetHeader
themes/FleetTheme.yaml the theme to paste in the Themes pane
sample/*.json three to five rows per list, to seed a first look
schema/ the official v3.0 schema (unmodified) and its licence note
flows/ FleetOutboxToLists and FleetDecide, their build and import sheets
data/ FleetAgent.xlsx, the list source and Excel fallback, and its generator
tests/
test_mobile_powerapp.py the guard over powerapp/
test_contract.py the pin, the examples, the lists, FleetDecide and the workbook against the contract
test_flows.py every Parse JSON schema in the flows against every pinned example
test_site.py the site: its lists and site scripts, formatters, flow, navigation, page sheets, artwork
The list columns, the flow signature and the deep-link parameters are the shared contract (v1) the
flows author and the laptop bridge build to: contract/fleet-mobile.v1.schema.json, pinned by
contract/PIN. The columns are also listed in tests/test_mobile_powerapp.py (and held equal to
the contract by tests/test_contract.py), and the lists themselves are created by data/README.md.
The quickest way is the Copilot build, build/README.md. A GitHub Copilot agent walks you through this same sheet: it writes the list flow and both flows as packages you import, puts each paste below on your clipboard in order, reads back the run reports and Studio's errors, and fixes the sources. It uses no MCP server and no agent plugin, which this organisation blocks; every click is yours, in the browser. The sheet below is the same work without the agent.
Everything below is done once, by hand, in Power Apps Studio. Nothing in this folder is imported as a file: whole screens are pasted through Code view (GA since March 2025), the App object is typed into the formula bar, and data sources and display settings are Studio-side settings the YAML cannot carry.
-
Create the five lists on the SharePoint site the flows write to:
FleetAttention,FleetApprovals,FleetDecisions,FleetNotifications,FleetHeartbeat, with the columns indata/README.md. Every column is text (single line unless the sheet says multi-line) andOperatorwithTitleis the key. Lock all five to the site's Owners (data/README.md§Owners only). To seed a first look, load the rows inpowerapp/sample/*.json; replaceoperator@example.com(the heartbeat'sTitleand everyOperator) with your own UPN, or the app shows you nothing under My fleet and warns about a mismatch (which is itself a fair first test of the banner). -
Create the app. Power Apps > Create > Blank app > Blank canvas app, name
FleetAgent, format Tablet. Then Settings:- Updates > New: Modern controls and themes on; Enhanced component properties on.
- Display: Scale to fit OFF, Lock aspect ratio OFF, Lock orientation OFF, Apply. Reason: these three are not in the YAML (the schema has no slot for them) and the screens are written as responsive containers that only behave with all three off; Microsoft's own guidance for one app on phone, tablet and web is a Tablet app with exactly these settings.
- General: leave the data row limit at 500.
-
Add data. Data > Add data > SharePoint > your site > tick the five lists. Their names in the Data pane must read exactly
FleetAttentionand so on; every formula names them that way. Then the Power Automate pane > Add flow >FleetDecide(the Power Apps (V2) flow the flows author built; inputsKind, ApprovalId, Repo, Decision, Reason, Message, AnswersJson, Digestas text,ExpiresSecondsas number,Deviceas text; it responds withok(Yes/No),nonce,inboxFile,error). -
Theme. Themes pane > Add a theme > Paste theme > paste the whole of
powerapp/themes/FleetTheme.yamlselect
FleetTheme. The seed colour#58A6FFis the accent of the desk'sdarkpalette in this-next-please'sagentdata/theme.py; the app usesApp.Theme.Colors.Primaryonly for the thin strip under each header, and neutral colours for all text (see Contrast below). -
Paste, in this order (each file is one paste; the order makes every reference resolve):
- Components, through the Components tab of the tree view: right-click > Paste code, one file
each, in the order
Components/EmptyState.pa.yaml,Components/KeyValueRow.pa.yaml,Components/FleetHeader.pa.yaml. - Screens, through the Screens tab:
SettingsScreen,ReplyScreen,DecideScreen,ApprovalScreen,AgentScreen,HomeScreen. This is the order that leaves the fewest names unresolved at each step: a formula that names a screen not yet pasted shows "name isn't recognized" until that screen exists (every back button namesHomeScreen, so a few of those are unavoidable), andHomeScreenlast clears them all. A pasted screen arrives beside the defaultScreen1; onceHomeScreenis in, deleteScreen1. If a paste is refused, read the message against the triage table in step 10 before changing a line. Paste a whole file each time; a partial paste leaves the screen half built.
- Components, through the Components tab of the tree view: right-click > Paste code, one file
each, in the order
-
The App object, by hand. Code view cannot paste the App object. Select App in the tree view and copy each property from
powerapp/src/App.pa.yamlinto the formula bar, without the leading=:StartScreen,BackEnabled,OnError, andFormulas(the whole block, comments included). Do this after the screens exist, becauseStartScreennames three of them. LeaveOnStartempty. -
Version suffixes. The sources omit every
@versionon purpose. If Studio insists on one for a type ("template-version conflict" or "must reference the same version"), open code view on any control of that type, read the exactControl:value it prints, and put that value on every instance of the type across all files. Never invent a version and never mix two for one type. -
Save, publish, share the app with every operator (as User; decline sharing the lists, which stay Owners-only), then open it once in Power Apps mobile on the phone: push notifications reach only a user who has opened the app in the last 30 days.
-
Test checklist. Use the preview device picker at 390x844, 844x390, 820x1180 and 1180x820 after publishing (the authoring canvas does not reflow), and then a real phone in both orientations:
- Home: the tabs switch the four lists; a row tap opens the agent or the approval; on widths of 900 px and more the right pane shows the selected repository instead of navigating.
- The banner appears when the laptop bridge is stopped for more than three heartbeats and when you sign in as an account other than the laptop's operator.
- Approval: Approve and Deny are disabled once the row is decided or past its expiry; Deny needs
a reason before Send is enabled; a sent decision shows the flow's
errorverbatim when it fails. - Reply: disabled with the laptop's sentence when the session is external.
- Show the team's on Home lists the sample's
colleague@example.comrow, and Approve, Deny and Reply are disabled on it, naming whose it is. - Deep links: a push with
screen=approval&approvalId=<id>opens the approval; withscreen=agent&repo=<alias>the agent. Parameters are read at launch only; a link into an app that is already open needsrestartApp=true(seeresearch_notes/Mobile fleet scope/notifications_intune_powerapps.md). - Accessibility checker: 0 errors and 0 warnings on every screen; every button is at least 48 px.
- Rotate the phone: nothing is clipped and no screen scrolls sideways.
-
Troubleshooting. The parse errors Studio reports on a paste, and what each one means:
Studio says Cause Fix Power Fx expressions must start with '='a property value without =every value starts with =mapping values are not allowed/ a formula cut at a colon:inside a plain single-line formulasingle-quote the value or make it a |-blockExpected 'MappingEnd', got 'Scalar'wrong indentation two spaces per level, exactly as in the file a control silently missing a Children:item without the leading-every child is - Name:Named object value cannot be nullan empty Properties:remove it or give it a property duplicate key error the same property or control name twice rename a multi-line quoted string error a quoted formula spanning lines use a |-blockfound character that cannot start any tokena tab character spaces only template-version conflict two versions for one control type step 7 name isn't recognizedonFleetAttentionetc.a data source not yet added step 3, then paste again name isn't recognizedonFleetDecide.Runthe flow is not added to the app Power Automate pane, step 3 name isn't recognizedonAgentScreenorHomeScreenthat screen is not pasted yet finish step 5; the error clears when the screen exists powerapp/NOTES.mdlists every place where the research could not verify a property form; if a paste fails on one of those lines, that list says what to try.
pac canvas pack --layout SourceCodebuilds an.msappfrom aSrc/*.pa.yamltree offline (pac canvas pack --sources <dir> --msapp FleetAgent.msapp --layout SourceCode), which Studio then opens with File > Open > Browse. Thepack/unpackcommands are deprecated in favour of Git integration, and whether they accept a hand-authored tree that was never unpacked is unverified.- The Canvas Authoring MCP server and Microsoft's
canvas-appsandpower-automateplugins would push the.pa.yamlfiles into a live coauthoring session and create the flows directly. This organisation blocks MCP servers and agent plugins, so nothing here uses them: the Copilot build pastes through code view and imports packages instead (build/README.md).
Every text colour in the app is a neutral RGBA chosen against the fill it sits on, measured with
this-next-please's agentdata.theme.contrast_ratio:
| Text | On | Ratio |
|---|---|---|
body #242424 |
screen #FAFAFA |
14.9:1 |
body #242424 |
card #FFFFFF |
15.5:1 |
muted #616161 |
screen #FAFAFA |
5.9:1 |
muted #616161 |
card #FFFFFF |
6.2:1 |
banner #242424 |
banner #FFF4CE |
14.1:1 |
payload #242424 |
payload surface #F3F2F1 |
13.9:1 |
App.Theme.Colors.Primary (seed #58A6FF) is 2.5:1 against white, so it is never used for text.
Badges, buttons and the tab list take their colours from the theme's accessible ramp; status is
always carried by the word on the badge, never by its colour alone.
python -m pip install -r requirements-dev.txt
python -m pytest -qholds the sources to the schema's shape and to the app's ground rules, and the lists, the flows and
the workbook to the pinned contract. CI runs the same on Ubuntu and on Windows with
core.autocrlf=true. The full
Draft 7 validation against powerapp/schema/pa.schema.yaml needs jsonschema (in requirements-dev.txt):
merge every src/**/*.pa.yaml into one document, as the schema says all files are logically
combined, and validate it with jsonschema.Draft7Validator(schema).iter_errors(merged); skip
check_schema, because the official schema's PCF-name regex does not compile under Python's re
(NOTES.md, item 26).