Skip to content

Commit 73d1181

Browse files
akshay-vizCopilot
andcommitted
docs(model-apps): move persona-probe detail out of SKILL.md into a reference
Asked whether adding a step to app-builder was the right place for this. The step itself is right -- without an entry point the script is undiscoverable, and there is no validation skill to host it (deferred by design until the loop it would wrap actually exists). The 19 lines of prose in SKILL.md were not. `skills/app-builder/SKILL.md` was already 508 lines against the plugin's own "keep SKILL.md under 500 lines" guideline before this feature, and the block pushed it to 527. The same guideline names the fix: "use progressive disclosure -- SKILL.md for workflow, reference files for details". So the invocation and the one-line reason stay in the phase; prerequisites, how to read `inconclusive`, why it probes the negative direction, and the scope limit move to `references/persona-validation.md`. That page also states the two-layer split -- why the metadata check stays in the build gate and the probe does not -- which is the actual architectural answer and had no home in either file. Net -6 lines in SKILL.md, so this does NOT get the file under 500; it was over budget beforehand and that is a pre-existing condition worth fixing separately. Docs only; no behaviour change. 1522 tests still pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 42626da2-b66f-4162-acaa-b1127ef23d89
1 parent c4befeb commit 73d1181

3 files changed

Lines changed: 80 additions & 12 deletions

File tree

plugins/model-apps/AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ references/ ← Shared reference docs
414414
localization.md ← Multi-language + RTL pattern (loaded conditionally)
415415
supported-dependencies.md ← Versioned package list for generated pages
416416
troubleshooting.md ← Deployment/runtime/env issues
417+
persona-validation.md ← app-builder: probe-persona prerequisites, reading `inconclusive`, scope limit
417418
verified-icons.txt ← ~5000 Fluent UI icon names; Grep-validated by page-builder
418419
samples/ ← Example .tsx files (13 samples) plus app-builder spec samples
419420
scripts/
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Persona validation — what each persona can actually DO
2+
3+
Loaded on demand by `/app-builder` Phase 3. Verifying a build proves the app matches its spec; this
4+
page is about the separate question of whether each **persona** can actually work in it.
5+
6+
## Two layers, deliberately separate
7+
8+
| | `verify-model-app.js``role-privileges` | `probe-persona.js` |
9+
|---|---|---|
10+
| Proves | the role **holds** the declared privileges | the persona can **actually perform** the operation |
11+
| Also depends on | nothing | record ownership, business unit, team membership, sharing, plug-ins |
12+
| Cost | free — metadata reads during a verify that already runs | N × M round trips |
13+
| Prerequisites | none | a test user + `prvActOnBehalfOfAnotherUser` |
14+
| Answer shape | binary | can legitimately be **inconclusive** |
15+
| So it is | part of the **build gate**, always on | **opt-in**, run when you want it |
16+
17+
They are not redundant. A role can hold every declared privilege and still leave the persona unable
18+
to work — depth interacts with who owns the records, which business unit they sit in, what teams the
19+
user belongs to, what has been shared, and what server-side plug-ins reject. `roleprivileges` shows
20+
none of that, so only executing a real operation answers it.
21+
22+
Keeping the metadata check in the build gate is what makes it free and unconditional; moving it out
23+
would restore the hole it was added to close — a role row exists, verify reports clean, and nothing
24+
checks what it grants.
25+
26+
## Running it
27+
28+
Read-only. It changes nothing.
29+
30+
```bash
31+
node "${PLUGIN_ROOT}/scripts/probe-persona.js" --env <envUrl> --spec @<working-dir>/app-spec.json
32+
```
33+
34+
`--allow-mutations` additionally *plans* create/write/delete probes. It does **not** execute them —
35+
exercising a write to verify it needs fixture creation and cleanup, which is a separate design.
36+
37+
## Prerequisites
38+
39+
It reports clearly and exits rather than guessing when these are unmet:
40+
41+
- the persona declares `assignTo.users[]` (already a `systemuserid`, which is what the impersonation
42+
header takes — no directory lookup and no application user needed);
43+
- the signed-in user holds **`prvActOnBehalfOfAnotherUser`**, assigned **directly** — a
44+
team-inherited grant does not satisfy it.
45+
46+
## Reading the output
47+
48+
- **`pass`** — the operation behaved as declared.
49+
- **`fail`** — a declared privilege did not work, or an entity the persona never declared *was*
50+
readable (an over-broad role).
51+
- **`inconclusive`** — the probe **proved nothing either way**. It is *not* a pass. Inconclusive
52+
results do not fail the run, because they are genuine unknowns and failing on them would train you
53+
to ignore the tool — but they are always counted, so an all-inconclusive run cannot look clean.
54+
55+
The most common inconclusive is an empty `200` on a negative probe: Dataverse answers *"no privilege"*
56+
with `403` but *"narrower scope"* with a filtered `200`, which is indistinguishable from an authorized
57+
read of an empty table. Seed a row owned by another user to disambiguate.
58+
59+
## Why it probes the negative direction
60+
61+
For each persona it also reads an entity that **another** persona declares and this one does not.
62+
63+
An over-broad role is invisible from the inside: every operation the user tries simply succeeds. It
64+
can only be detected by trying something that *should* fail. `appmodule` is never probed negatively —
65+
the build injects it for every persona, so it would report a failure on every run.
66+
67+
## What a green run does NOT mean
68+
69+
This exercises the **Web API**. It says nothing about UCI navigation, which form opens, field or
70+
control visibility, client-side script, the command bar, layout, or accessibility.
71+
72+
**A green run means the data operations are authorized — not that the app works.** Those still need a
73+
browser pass or a human.

plugins/model-apps/skills/app-builder/SKILL.md

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -309,23 +309,17 @@ node "${PLUGIN_ROOT}/scripts/verify-model-app.js" --env <envUrl> --spec @<workin
309309
```
310310

311311
**Optional — probe what each persona can actually DO.** Verify's `role-privileges` check is a
312-
*metadata* comparison: it proves the deployed role HOLDS the declared privileges. It cannot prove the
313-
persona can perform the operation, because that also depends on record ownership, business-unit
314-
placement, team membership, sharing, and server-side plug-ins. To check that, run real reads **as each
315-
persona** using Dataverse impersonation (read-only; it changes nothing):
312+
*metadata* comparison; it cannot prove the persona can perform the operation, which also depends on
313+
record ownership, business unit, team membership, sharing and plug-ins. To check that, run read-only
314+
authorization probes **as each persona** via Dataverse impersonation:
316315

317316
```bash
318317
node "${PLUGIN_ROOT}/scripts/probe-persona.js" --env <envUrl> --spec @<working-dir>/app-spec.json
319318
```
320319

321-
It also probes the **negative** direction — reading an entity another persona declares and this one
322-
does not — which is the only way to catch an over-broad role, since from the inside every operation
323-
the user tries simply succeeds. Prerequisites (it reports clearly and exits rather than guessing when
324-
they are unmet): the persona declares `assignTo.users[]`, and the signed-in user holds
325-
`prvActOnBehalfOfAnotherUser` assigned **directly**. Results marked *inconclusive* proved nothing
326-
either way and are **not** passes. It exercises the Web API, so a green run means the data operations
327-
are authorized — **not** that the app works: navigation, form and control visibility, client script
328-
and layout still need a human or a browser pass.
320+
Prerequisites, how to read `inconclusive` (it is **not** a pass), and the scope limit — a green run
321+
means the data operations are authorized, **not** that the app works — are in
322+
[persona-validation.md](../../references/persona-validation.md).
329323

330324
Then open the app in the browser. Refine `app-spec.json` and re-run Phase 2 to iterate.
331325

0 commit comments

Comments
 (0)