@salesforce/afv-skills 1.46.0 → 1.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/agentforce-observe/SKILL.md +32 -4
- package/skills/agentforce-observe/references/ahm-alerts.md +719 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +275 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/README.md +32 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls +75 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls-meta.xml +5 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/interview-answers.json +13 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +10 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/create.json +20 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/update.json +16 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +273 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +236 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/reference-example-set-comment-value.md +132 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +211 -0
- package/skills/dx-devops-project-manage/SKILL.md +197 -0
- package/skills/dx-devops-project-manage/examples/common-workflows.md +197 -0
- package/skills/dx-devops-project-manage/references/cli-commands.md +295 -0
- package/skills/dx-devops-project-manage/scripts/create-project.sh +48 -0
- package/skills/dx-devops-project-manage/scripts/list-projects.sh +51 -0
- package/skills/dx-devops-project-manage/scripts/update-project.sh +96 -0
- package/skills/education-cloud-academic-calendar-generate/SKILL.md +225 -0
- package/skills/education-cloud-academic-calendar-generate/examples/quarter-calendar.json +47 -0
- package/skills/education-cloud-academic-calendar-generate/examples/sample-output.md +57 -0
- package/skills/education-cloud-academic-calendar-generate/examples/semester-calendar.json +54 -0
- package/skills/education-cloud-academic-calendar-generate/references/calendar-systems.md +127 -0
- package/skills/education-cloud-academic-calendar-generate/references/date-validation.md +222 -0
- package/skills/education-cloud-academic-calendar-generate/references/foundation_prerequisites.md +40 -0
- package/skills/education-cloud-academic-calendar-generate/scripts/validate_calendar_dates.py +143 -0
- package/skills/education-cloud-course-catalog-migrate/SKILL.md +321 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas-detail.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/large-catalog-handling.md +42 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/batch_courses.py +36 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_linked_courses.py +51 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_modality_variants.py +48 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/resolve_api_version.py +43 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/split_course_code.py +39 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/validate_completeness.py +54 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +3 -5
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +177 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/agent-and-subagents.md +151 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/customer-narration.md +34 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/execution-model.md +54 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/flows.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/grounding.md +199 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/permissions.md +183 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/platform-enablement.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +158 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/routing.md +141 -0
- package/skills/experience-cms-brand-apply/SKILL.md +5 -5
- package/skills/experience-cms-brand-create/SKILL.md +2 -2
- package/skills/experience-cms-content-generate/SKILL.md +1 -0
- package/skills/experience-cms-content-render/SKILL.md +173 -0
- package/skills/experience-cms-content-render/assets/angular/DetailPage.component.ts +25 -0
- package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +133 -0
- package/skills/experience-cms-content-render/assets/angular/TypeList.component.ts +38 -0
- package/skills/experience-cms-content-render/assets/angular/TypeRenderer.component.ts +90 -0
- package/skills/experience-cms-content-render/assets/angular/cms-content.component.ts +248 -0
- package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +100 -0
- package/skills/experience-cms-content-render/assets/react/DetailPage.tsx +20 -0
- package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +129 -0
- package/skills/experience-cms-content-render/assets/react/TypeList.tsx +40 -0
- package/skills/experience-cms-content-render/assets/react/TypeRenderer.tsx +64 -0
- package/skills/experience-cms-content-render/assets/react/heuristicRenderer.tsx +310 -0
- package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +129 -0
- package/skills/experience-cms-content-render/assets/shared/cmsContentType.ts +49 -0
- package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +96 -0
- package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +55 -0
- package/skills/experience-cms-content-render/references/bulk-loading.md +60 -0
- package/skills/experience-cms-content-render/references/codegen-guardrails.md +111 -0
- package/skills/experience-cms-content-render/references/detail-pages.md +87 -0
- package/skills/experience-cms-content-render/references/embed-recipes.md +127 -0
- package/skills/experience-cms-content-render/references/failure-modes.md +96 -0
- package/skills/experience-cms-content-render/references/heuristic-render-rules.md +131 -0
- package/skills/experience-cms-content-render/references/init-scaffold.md +122 -0
- package/skills/experience-cms-content-render/references/interaction-model.md +173 -0
- package/skills/experience-cms-content-render/references/package-api.md +106 -0
- package/skills/experience-cms-content-render/references/schema-sync.md +114 -0
- package/skills/experience-cms-content-render/references/styling-scopes.md +65 -0
- package/skills/experience-cms-content-render/references/verify.md +49 -0
- package/skills/experience-cms-content-type-generate/SKILL.md +2 -2
- package/skills/experience-content-media-stock-image-search/SKILL.md +5 -4
- package/skills/experience-search-coordinate/SKILL.md +198 -0
- package/skills/experience-search-coordinate/assets/search-payload-template.json +25 -0
- package/skills/experience-search-coordinate/references/content-route.md +313 -0
- package/skills/experience-search-coordinate/references/content-type-discovery.md +57 -0
- package/skills/experience-search-coordinate/references/media-route.md +172 -0
- package/skills/experience-search-coordinate/references/scope-resolution.md +14 -0
- package/skills/experience-ui-bundle-localize/SKILL.md +1 -1
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +5 -3
- package/skills/experience-ui-bundle-project-generate/SKILL.md +18 -14
- package/skills/experience-ui-bundle-project-generate/references/angular-project-generate.md +22 -0
- package/skills/experience-ui-bundle-project-generate/references/react-project-generate.md +20 -0
- package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +58 -54
- package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/graphiti-cli.md +2 -2
- package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/rest-and-integration.md +2 -1
- package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +6 -0
- package/skills/experience-ui-bundle-site-generate/SKILL.md +59 -8
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-digital-experience.md +8 -3
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-language-settings.md +120 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +336 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +143 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +127 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-deploy-commands.md +116 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +111 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-overview.md +312 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +171 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-state-tracking.md +64 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-trigger-handlers.md +122 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-overview.md +335 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-user-provisioning-details.md +140 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +196 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +307 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +211 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/state-machine-and-changes.md +108 -0
- package/skills/life-sciences-kam-coordinate/SKILL.md +241 -0
- package/skills/life-sciences-kam-coordinate/references/orchestration-flow.md +152 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +79 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-deploy-commands.md +131 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-kam-config-records.md +85 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +112 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-overview.md +202 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +67 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-state-tracking.md +65 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-trigger-handlers.md +123 -0
- package/skills/life-sciences-kam-coordinate/references/stage-4-participant-role-and-sprint.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +337 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +248 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-ipad-validation-script.md +35 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-details.md +146 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-overview.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/state-machine-and-changes.md +114 -0
- package/skills/life-sciences-prerequisites-validate/SKILL.md +138 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-org-settings.md +190 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-user-and-package.md +211 -0
- package/skills/life-sciences-territory-configure/SKILL.md +217 -0
- package/skills/life-sciences-territory-configure/references/territory-metadata.md +262 -0
- package/skills/platform-dsar-policy-manage/SKILL.md +272 -0
- package/skills/platform-dsar-policy-manage/references/configure.md +106 -0
- package/skills/platform-dsar-policy-manage/references/export-and-history.md +123 -0
- package/skills/platform-dsar-policy-manage/references/gap-analysis-guide.md +150 -0
- package/skills/platform-dsar-policy-manage/references/gap-scan.md +129 -0
- package/skills/platform-dsar-policy-manage/references/headless-sor.md +59 -0
- package/skills/platform-dsar-policy-manage/references/report-format.md +59 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/__init__.py +0 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/test_validate_policy_tree.py +76 -0
- package/skills/platform-dsar-policy-manage/scripts/validate-policy-tree.py +130 -0
- package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +359 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/official-examples.md +69 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/scenarios.md +187 -0
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +20 -27
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +47 -27
- package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +13 -9
- package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +104 -45
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: platform-dsar-policy-manage
|
|
3
|
+
description: "Configure, run, and audit DsarPolicy Right-to-Portability exports end to end: author the data map over a subject's related records, resolve a request's subject (email/name/id) to a root-entity record, run an export against an ACTIVE policy (poll a couple of times, then ask before continuing; download once terminal), and read run history. Use when the user needs to set up, edit, execute, or inspect a DsarPolicy or Right-to-Portability (RTP) export, or audit which personal data a policy does not cover yet. TRIGGER when: the user configures a DsarPolicy or RTP data map, authors DsarPolicyPath or DsarPolicyField metadata, runs or exports a subject's portable data, downloads an RTP export file, reviews DsarPolicy run history, or asks what personal data the policy does not cover yet (coverage gap analysis). DO NOT TRIGGER when: the user wants to erase, delete, or remove a subject's records (RTP is portability export, not erasure — this skill deletes nothing), mask sandbox PII, or deploy unrelated metadata."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.7"
|
|
6
|
+
domains: ["Platform"]
|
|
7
|
+
minApiVersion: "68.0"
|
|
8
|
+
cliTools:
|
|
9
|
+
- tool: ["python3"]
|
|
10
|
+
semver: ">=3.10.0"
|
|
11
|
+
- tool: ["sf"]
|
|
12
|
+
semver: ">=2.0.0"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# platform-dsar-policy-manage: DsarPolicy Right to Portability, end to end
|
|
16
|
+
|
|
17
|
+
Operate a **`DsarPolicy`** — the metadata driving a **Right-to-Portability (RTP)** export — across
|
|
18
|
+
its lifecycle: **configure** the tree, **run** an export, **audit** runs. A `DsarPolicy` is a
|
|
19
|
+
*read/export map*: it names which of a subject's records an export collects. It **never deletes
|
|
20
|
+
anything**. The value here is **judgment**, not XML plumbing.
|
|
21
|
+
|
|
22
|
+
> **FIRST LINE OF EVERY RESPONSE — do not narrate the skill.** Your first output token is *work or
|
|
23
|
+
> the answer*, never a play-by-play. Do **not** open with "This is a coverage-gap audit / Workflow
|
|
24
|
+
> D…", "Let me read the skill's guide/mechanics…", "Per rule #1…", or "Let me discover the SOR…".
|
|
25
|
+
> Don't name the workflow, announce which reference files you're about to read, or describe your
|
|
26
|
+
> method before doing it — just start doing it (call the tool) and, when done, present the result.
|
|
27
|
+
> This is load-bearing call #8, stated up front because it governs your very first token. A
|
|
28
|
+
> plain-language sentence about *the user's data/org* is fine; a sentence about *the skill, its
|
|
29
|
+
> workflows, rules, tools, or files* is not.
|
|
30
|
+
|
|
31
|
+
## Load-bearing calls (get these right or the task fails)
|
|
32
|
+
|
|
33
|
+
These override any leading phrasing in the request. Read them first, every time. **Call #1 is the
|
|
34
|
+
literal first action of every task — before you pick a workflow, before any `sf` command.**
|
|
35
|
+
|
|
36
|
+
1. **Route through the SOR tool FIRST — before any `sf`/endpoint call, no exceptions.** The very
|
|
37
|
+
first action of *every* task here — including a trivial read-only "does a policy exist / list the
|
|
38
|
+
policies" lookup — is to scan **this run's available tools** for a capability-routing tool exposing
|
|
39
|
+
**discover / describe / dispatch** verbs (project-codey / Headless 360 today; any host prefix such
|
|
40
|
+
as `mcp__<host>__discover`; **match the verb shape, not a fixed name**). If one is present, that is
|
|
41
|
+
the entry move: `discover → describe → dispatch` the **`DsarPolicyManager`** SOR (owner Privacy
|
|
42
|
+
Center; `isActive` gates execute/edit/delete) and reject look-alikes (data mask, generic consent,
|
|
43
|
+
subject *erasure*). Do **not** reach for `sf` first — grabbing `sf` on reflex because the task
|
|
44
|
+
"looks like a quick lookup" is the top recurring miss, and it is wrong even when it would work.
|
|
45
|
+
`sf` is the fallback **only** when no routing tool is in reach (checking `sf org list` to see which
|
|
46
|
+
orgs are authenticated locally is fine — that is a client-side auth check, not a SOR call). Every
|
|
47
|
+
`sf`/`salesforce-api-context` command shown in the workflows below is the *no-routing-tool*
|
|
48
|
+
spelling; when the routing tool is present, dispatch the equivalent operation through it instead.
|
|
49
|
+
Detail: `references/headless-sor.md`.
|
|
50
|
+
2. **Export, never erasure.** A run produces an **export file**; it does not erase/redact/delete the
|
|
51
|
+
subject. If the request frames it as deletion ("remove them", "erase", "clear out"), **correct
|
|
52
|
+
that first** and never report the subject as erased. Erasure is a separate mechanism — if that's
|
|
53
|
+
the goal, say so and stop. You may still export if authorization is clear, after correcting the
|
|
54
|
+
premise.
|
|
55
|
+
3. **Do not guess personal data.** Which objects/relationships hold *the subject's* data is the
|
|
56
|
+
**admin's** classification call. If the request delegates it ("figure out what counts", "wire the
|
|
57
|
+
whole thing up"), **do not author a guessed tree** — enumerate the decisions the admin must make
|
|
58
|
+
(roots; per-relationship follow/stop; fields), mark any proposal *pending admin disposition*. An
|
|
59
|
+
over-broad guess exports other people's records.
|
|
60
|
+
4. **Caps are hard — name the cap and stop; don't graph-walk.** Tree caps: **10 children/path, depth
|
|
61
|
+
10, 200 nodes**. An unbounded request ("everything", "the whole graph") cannot fit. Do **not**
|
|
62
|
+
enumerate the object graph to "try" (never finishes, burns the turn). Name the specific cap
|
|
63
|
+
exceeded and offer a bounded alternative (split policies, prune branches). Never silently truncate.
|
|
64
|
+
5. **Poll a couple of times, then ask — don't block on terminal.** The run handle serves status
|
|
65
|
+
**and** file. An early *getfile* returns `NOT_FOUND` / `"This file isn't ready yet"` — the
|
|
66
|
+
contract working, not a failure. Poll ≈2–3×; if still not terminal, **stop and ask the user
|
|
67
|
+
whether to keep polling** — don't loop. A run can sit non-terminal indefinitely on downstream
|
|
68
|
+
async processing — **platform / Tool Factory territory, not this skill's to diagnose or reach
|
|
69
|
+
into**; report status in plain terms (**running / completed / errored**) and let the user decide.
|
|
70
|
+
Never download before terminal. A **failed run can return HTTP 201** — read the **envelope /
|
|
71
|
+
`RequestStatus`** in the body, not the HTTP code. getfile segment is **`dsr`**, not `dsar`.
|
|
72
|
+
6. **History is a read.** Run history = a **SOQL query over `DsarPolicyLog`**. Not `installListView`,
|
|
73
|
+
not a UI list; it starts no run.
|
|
74
|
+
7. **Never auto-activate or republish.** Activation is a deliberate, human-confirmed step. Editing an
|
|
75
|
+
ACTIVE policy needs it deactivated first — **deactivate → edit → stop, report, get explicit user
|
|
76
|
+
confirmation before reactivating**. New policies stay INACTIVE until the user activates.
|
|
77
|
+
Auto-republishing breaks the reviewable audit trail disputes depend on.
|
|
78
|
+
8. **Work silently — never narrate the skill's internals (applies to every workflow; see the
|
|
79
|
+
masthead above).** The user asked a domain question; answer it, don't describe how the skill
|
|
80
|
+
answers it. Run the tools and present the result — **no intermediate narration** of: that you are
|
|
81
|
+
about to read / are reading the skill's guide, mechanics, or reference files ("let me read the
|
|
82
|
+
skill's guide and mechanics"); **which workflow this is** ("this is a coverage-gap audit / Workflow
|
|
83
|
+
D", "Workflow C-style read") — the user does not think in workflow letters, so never name one;
|
|
84
|
+
which SOR/routing tool you're using or that you're "loading schemas / discovering / dispatching";
|
|
85
|
+
the load-bearing rule numbers ("per rule #1"); the reject-look-alikes step; the operation graph or
|
|
86
|
+
GET-vs-dispatch plumbing (`getAccessInfo`, `dispatch_readonly`, etc.). These steer *how you work* —
|
|
87
|
+
they are not status updates. (The harness still shows its own plain tool-call lines; that is fine —
|
|
88
|
+
just don't add your own play-by-play.) **What the user DOES see:** the final answer, and — where a
|
|
89
|
+
call requires it — a plain-language question (`AskUserQuestion`) or a short scope/consent line about
|
|
90
|
+
*their org* ("I only listed policies; I changed nothing", "this exports, it doesn't delete"). Rule
|
|
91
|
+
of thumb: a sentence about **their data/org** can be user-facing; a sentence about **the skill, its
|
|
92
|
+
workflows, steps, tools, rules, or files** stays internal. Workflow D's method preamble is the one
|
|
93
|
+
allowed "here's how I'll do it" line, and even it must be plain-language about *the audit approach*
|
|
94
|
+
("I'll look one level out from your policy's objects and flag fields that might hold personal
|
|
95
|
+
data") — it names **no** workflow letter, rule, tool, or file. That preamble is *not* a licence to
|
|
96
|
+
say "this is Workflow D" or "let me read the mechanics".
|
|
97
|
+
|
|
98
|
+
## The DsarPolicy object model
|
|
99
|
+
|
|
100
|
+
Each entity is reached a **different** way — guessing the surface is the top time-sink.
|
|
101
|
+
|
|
102
|
+
| Entity | What it is | How you reach it |
|
|
103
|
+
|--------|-----------|------------------|
|
|
104
|
+
| `DsarPolicy` | Policy shell + lifecycle (ACTIVE/INACTIVE) | **Metadata API** |
|
|
105
|
+
| `DsarPolicyPath` | A tree node: a root object, or a parent→related relationship | **Metadata API** (child of `DsarPolicy`) |
|
|
106
|
+
| `DsarPolicyField` | A field collected at a path | **Metadata API** (child of a path) |
|
|
107
|
+
| `DsarPolicyLog` | Run log (one row per run) | **Standard SOQL** |
|
|
108
|
+
| Execute an export | — | **Connect DSR endpoint** (`POST`) |
|
|
109
|
+
| Status / getfile | — | **Connect DSR endpoint** on the handle; getfile segment `dsr` |
|
|
110
|
+
|
|
111
|
+
Resolve the exact Connect route/version at run time via `salesforce-api-context` (or `sf`). Don't
|
|
112
|
+
`sf sobject describe DsarPolicy*` — the tree is metadata; only `DsarPolicyLog` answers standard
|
|
113
|
+
SOQL. On an MCP surface, each row is one `DsarPolicyManager` operation.
|
|
114
|
+
|
|
115
|
+
## Pick the workflow (by the verb)
|
|
116
|
+
|
|
117
|
+
| Want to… | Run | Ends when |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| Set up / edit a policy tree | **A — Configure** | Bounded policy authored INACTIVE; or an unbounded request's cap is named / an under-specified one's decisions enumerated — and stops |
|
|
120
|
+
| Run an export for a subject | **B — Export** | Subject resolved to a root Id, run status read (running/completed/errored), file located on success — or, if still running after a couple polls, the user is asked whether to continue |
|
|
121
|
+
| See past runs | **C — History** | Prior runs reported from `DsarPolicyLog`, no run started |
|
|
122
|
+
| Find PII not yet covered | **D — Coverage gap** | Candidates surfaced with per-field reasons, disposition left to the admin — read-only |
|
|
123
|
+
|
|
124
|
+
Mixed request → do the one asked; don't add an export to a configure, or a run to a history.
|
|
125
|
+
|
|
126
|
+
**Every workflow below assumes call #1 is already done** — you have routed through the discover/
|
|
127
|
+
describe/dispatch SOR tool (or confirmed none is in reach). The `sf`/`salesforce-api-context`
|
|
128
|
+
commands in each workflow are the *no-routing-tool* spelling; with the routing tool present, dispatch
|
|
129
|
+
the equivalent operation through it.
|
|
130
|
+
|
|
131
|
+
## Workflow A — Configure
|
|
132
|
+
|
|
133
|
+
Recipe (metadata shape, relationship/field resolution, lifecycle transitions): `references/configure.md`.
|
|
134
|
+
|
|
135
|
+
1. **Classify first — short-circuit before any describe.** Three shapes; two never reach authoring:
|
|
136
|
+
- **Unbounded** ("everything", "whole graph") — can't fit caps. Don't describe/walk. Write the
|
|
137
|
+
**cap-refusal report** (call #3), stop.
|
|
138
|
+
- **Under-specified** — delegates classification, no root/field named. Don't author a guess.
|
|
139
|
+
Write the **elicitation report** (the admin's decisions; support **multiple roots**; proposals
|
|
140
|
+
*pending disposition*), stop.
|
|
141
|
+
- **Bounded & specified** — named roots/relationships/fields fitting the caps → continue.
|
|
142
|
+
2. **Confirm type + describe named objects only** (don't assume names, don't expand beyond what's named):
|
|
143
|
+
```bash
|
|
144
|
+
sf org list metadata --metadata-type DsarPolicy --target-org <alias> --json
|
|
145
|
+
sf sobject describe --sobject <NamedObject> --target-org <alias> --json
|
|
146
|
+
```
|
|
147
|
+
If `DsarPolicy` can't be listed/described, surface it and stop (accepted terminal outcome).
|
|
148
|
+
3. **Validate the tree** (don't eyeball caps/names):
|
|
149
|
+
```bash
|
|
150
|
+
python3 scripts/validate-policy-tree.py <tree.json>
|
|
151
|
+
```
|
|
152
|
+
Checks children≤10, depth≤10, nodes≤200, and devname `[a-zA-Z]+[a-zA-Z0-9_]*`.
|
|
153
|
+
4. **Author metadata INACTIVE** under `${outputDir}`, faithful to the sanctioned strategy — add no
|
|
154
|
+
unapproved path, drop none approved.
|
|
155
|
+
> **Lifecycle gate:** INACTIVE to edit/delete, ACTIVE to execute. Change an ACTIVE policy by
|
|
156
|
+
> **deactivate → edit → STOP**; get explicit user confirmation before reactivating (call #7).
|
|
157
|
+
5. **Deploy (best effort):** `sf project deploy start --source-dir <outputDir> --target-org <alias> --json`.
|
|
158
|
+
Deploy may fail where the type isn't fully enabled — surface the raw error + prerequisite; don't
|
|
159
|
+
fake success. The classification work is valid regardless.
|
|
160
|
+
6. **Report** the outcome reached — **cap-refusal** / **elicitation** / **authored** — per
|
|
161
|
+
`references/report-format.md`. Never a truncated tree called "complete".
|
|
162
|
+
|
|
163
|
+
## Workflow B — Run an export
|
|
164
|
+
|
|
165
|
+
Endpoints, sample envelopes, poll/download sequence: `references/export-and-history.md`.
|
|
166
|
+
|
|
167
|
+
- **B0. Resolve the subject.** Requests arrive as **email / name / id**, not a `dataSubjectId`.
|
|
168
|
+
Resolve to the **Id of a record whose type is a ROOT** of the chosen policy (Account / Contact /
|
|
169
|
+
Individual / Lead / User) — e.g. SOQL `Lead`/`Contact` by `Email`. Confirm the type **is a policy
|
|
170
|
+
root** (execute matches only the root subtree of the subject's type — a non-root subject exports
|
|
171
|
+
nothing). On **0 / many / non-root** matches, stop and report; never execute a guessed Id.
|
|
172
|
+
- **B1. Preconditions.** Policy must be **ACTIVE** (else route to A). Resolve DSR routes via
|
|
173
|
+
`salesforce-api-context`.
|
|
174
|
+
- **Pick the policy deliberately:** named → use it; else the in-context policy; if none is
|
|
175
|
+
established or **multiple ACTIVE could match**, **ask to confirm** (e.g. *"Run `<policy>` for
|
|
176
|
+
`<subject>` — confirm?"*). Never pick silently.
|
|
177
|
+
- **Access:** on `401`/`403`, name the guard and stop — user perm `Consent.CAN_EXECUTE_DSAR_POLICY`,
|
|
178
|
+
org feature `Consent.hasDsarPortability`. State the poll-then-download ordering even on this
|
|
179
|
+
error path (accepted terminal outcome; don't retry blindly).
|
|
180
|
+
- **B2. Consent gate + correct deletion framing (call #2).** If framed as deletion, correct it first
|
|
181
|
+
(export, not erasure). If authorization is ambiguous, **ask** via `AskUserQuestion`; clear
|
|
182
|
+
authorization → proceed.
|
|
183
|
+
- **B3. Execute — trust the envelope, not the HTTP code.** POST execute, capture the **run handle**.
|
|
184
|
+
A failed run can return **HTTP 201** — read the envelope status; report failure if it says so.
|
|
185
|
+
- **B4. Poll ≈2–3×, then ask; download only after terminal (call #5).** Poll the handle (or
|
|
186
|
+
`DsarPolicyLog.RequestStatus`). Early `NOT_FOUND` / "not ready" is expected. Still not terminal
|
|
187
|
+
after a couple polls → **stop and ask** whether to keep polling; don't loop (non-terminal =
|
|
188
|
+
downstream async, not this skill's to diagnose). Report **running / completed / errored**.
|
|
189
|
+
- **B5. Download (terminal-success only) — segment `dsr`.** A `dsar` segment 404s. Report where the
|
|
190
|
+
export landed.
|
|
191
|
+
- **B6. Report** per `references/report-format.md`: resolved subject (id + type) and policy chosen
|
|
192
|
+
(+ that you asked if ambiguous); consent confirmed; **export not deletion**; outcome from the
|
|
193
|
+
envelope / run status in plain terms; file location on success; poll ordering explicit (couple of
|
|
194
|
+
polls, file only after terminal, asked if still running). State the ordering even if preconditions
|
|
195
|
+
blocked the run.
|
|
196
|
+
|
|
197
|
+
## Workflow C — History (a read)
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
sf data query --target-org <alias> \
|
|
201
|
+
--query "SELECT Id, DsarPolicyId, RequestStatus, CreatedDate FROM DsarPolicyLog WHERE DsarPolicyId = '<POLICY_ID>' ORDER BY CreatedDate DESC"
|
|
202
|
+
```
|
|
203
|
+
Report prior runs (when, status); "no prior runs" is valid. On absent log / `401`/`403`/`404`,
|
|
204
|
+
surface the raw error + prerequisite. Don't execute the policy; don't use `installListView` / a UI list.
|
|
205
|
+
|
|
206
|
+
## Workflow D — Coverage gap analysis (read-only audit)
|
|
207
|
+
|
|
208
|
+
*"What personal data isn't covered yet?"* Read-only, deterministic — surfaces **candidates** for the
|
|
209
|
+
admin; classifies nothing, adds/activates nothing. Script + rubric: `references/gap-analysis-guide.md`;
|
|
210
|
+
mechanics: `references/gap-scan.md`.
|
|
211
|
+
|
|
212
|
+
1. **Open with the plain-language method line (call #8)** — one sentence in the user's terms: from
|
|
213
|
+
the policy's objects, look **one level** out and flag fields that *may* be PII, **with a reason
|
|
214
|
+
each**; the admin decides. Do **not** say "Workflow D", "coverage-gap audit", or "let me read the
|
|
215
|
+
mechanics" — just state the approach in plain words and start.
|
|
216
|
+
2. **Read current coverage** (read-only) — gaps are relative to it.
|
|
217
|
+
3. **Scan one hop only, and cap the breadth** — describe each root + **at most ~5** of its
|
|
218
|
+
most privacy-relevant one-hop objects; depth-1 default, don't recurse. **Never read a raw
|
|
219
|
+
`--json` describe into the turn** — a full describe is ~100KB and the accumulated payloads
|
|
220
|
+
overflow the event stream, truncating the run before it writes the report (the top failure here).
|
|
221
|
+
Project every describe to `{name,type,label}` first (see `references/gap-scan.md`). If the
|
|
222
|
+
policy/type can't be read (feature off — the accepted env path), **stop at a single projected
|
|
223
|
+
root describe**; don't fan out. Write the report from the root + a couple of one-hop describes,
|
|
224
|
+
name the objects you skipped, offer them via the depth gate. **The written report is the
|
|
225
|
+
deliverable — draft it early and write it; don't gather everything first.**
|
|
226
|
+
4. **Flag candidates with a per-field reason** — compliance metadata > field type > name semantics.
|
|
227
|
+
Never "this *is* PII" (call #3).
|
|
228
|
+
5. **Report transparently** — objects scanned, each candidate + reason, covered vs newly surfaced,
|
|
229
|
+
and the **one-level limit** stated (even when empty).
|
|
230
|
+
6. **Gate depth** — go deeper only on explicit confirmation (warn: large output, heavy cost).
|
|
231
|
+
7. **Disposition, don't mutate** — ask which to add; adding routes to Workflow A (INACTIVE, stops for
|
|
232
|
+
confirmation before reactivating — call #7).
|
|
233
|
+
|
|
234
|
+
## Quick rules & gotchas
|
|
235
|
+
|
|
236
|
+
| Situation | Do |
|
|
237
|
+
|---|---|
|
|
238
|
+
| **Starting ANY task (even a trivial "list/does-a-policy-exist" read)** | **FIRST** scan this run's tools for a discover/describe/dispatch routing tool (project-codey/Headless 360; any `mcp__<host>__…` name — match the verb shape). Present → `discover`/`describe`/`dispatch` the `DsarPolicyManager` SOR. Do NOT open with `sf` on reflex. `sf` only when no routing tool is in reach (`sf org list` for local auth is exempt) |
|
|
239
|
+
| "Capture everything / whole graph" | Exceeds caps (10/10/200) — name the cap, offer split/prune; don't walk the graph |
|
|
240
|
+
| "What PII are we missing?" | Workflow D — read-only depth-1; candidates + reason; disposition to admin; add/activate nothing |
|
|
241
|
+
| Subject given by email/name, no Id | Resolve first (B0): query the policy's root entities; confirm a root type; 0 / many / non-root → stop |
|
|
242
|
+
| Multiple ACTIVE policies could match | Confirm which with the user before running — never silent |
|
|
243
|
+
| Run stuck `In Progress` after a couple polls | Downstream async (Tool Factory / platform), not the skill's to diagnose — report *still running*, ask whether to keep polling; don't loop |
|
|
244
|
+
| getfile "not ready" / `NOT_FOUND` | Expected pre-terminal — poll again; not a failure |
|
|
245
|
+
| HTTP 201 on execute | Not success — read the envelope status |
|
|
246
|
+
| getfile 404 | Segment must be `dsr`, not `dsar` |
|
|
247
|
+
| Just edited an ACTIVE policy | Don't auto-reactivate — stop, report, get explicit confirmation (call #6) |
|
|
248
|
+
| `sf sobject describe DsarPolicy` empty | Tree is Metadata-API; only `DsarPolicyLog` answers SOQL |
|
|
249
|
+
| `DsarPolicy` type absent | Surface + stop; don't fabricate |
|
|
250
|
+
| `401`/`403`/`404` or missing type | Name the prerequisite and stop; no blind retries |
|
|
251
|
+
|
|
252
|
+
## Output (write to `${outputDir}/report.md`)
|
|
253
|
+
|
|
254
|
+
Report only the workflow you ran; each command once; the key result in the first screenful.
|
|
255
|
+
**Be concise** — state each load-bearing point (poll ordering, export-not-deletion, the
|
|
256
|
+
one-level limit) **once**, not restated across an intro, an aside, and a next-steps list; keep it
|
|
257
|
+
well under ~150 lines and don't paste exhaustive per-object dumps. On a preflight-error path (feature
|
|
258
|
+
/ policy / subject absent), name the blocker + prerequisite, state the ordering once, and stop —
|
|
259
|
+
short. Per-workflow contracts (incl. the INACTIVE / confirmed-reactivation lifecycle and the poll
|
|
260
|
+
ordering): `references/report-format.md`.
|
|
261
|
+
|
|
262
|
+
## Reference index
|
|
263
|
+
|
|
264
|
+
| File | When |
|
|
265
|
+
|---|---|
|
|
266
|
+
| `references/headless-sor.md` | MCP surface: discover→describe→dispatch the SOR, reject look-alikes, `sf` fallback |
|
|
267
|
+
| `references/configure.md` | Metadata shape, root/relationship resolution, `<tree.json>` input, lifecycle, multi-root |
|
|
268
|
+
| `references/export-and-history.md` | DSR execute/status/getfile routes, envelopes, poll sequence, `dsr` segment, history query |
|
|
269
|
+
| `references/report-format.md` | Per-workflow report contracts |
|
|
270
|
+
| `references/gap-analysis-guide.md` | Workflow D: audit script, steps, candidate-flagging rubric |
|
|
271
|
+
| `references/gap-scan.md` | Workflow D mechanics: one-hop enumeration, diff, depth gate, report shape |
|
|
272
|
+
| `scripts/validate-policy-tree.py` | Deterministic cap + devname check before authoring |
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Configure a DsarPolicy — authoring reference
|
|
2
|
+
|
|
3
|
+
Deep detail for **Workflow A**. Read this when you actually author the policy tree. The judgment
|
|
4
|
+
(refuse-to-guess, cap-and-stop) lives in `SKILL.md`; this file is the mechanics.
|
|
5
|
+
|
|
6
|
+
## The object model you author
|
|
7
|
+
|
|
8
|
+
A `DsarPolicy` is a **tree** of paths rooted on the object(s) that identify the data subject.
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
DsarPolicy (the shell: DeveloperName, MasterLabel, lifecycle state)
|
|
12
|
+
└── DsarPolicyPath (root) (a root object the subject is identified on, e.g. Contact)
|
|
13
|
+
├── DsarPolicyField (a field collected at this path)
|
|
14
|
+
├── DsarPolicyField
|
|
15
|
+
└── DsarPolicyPath (child) (a relationship FROM this object TO a related object)
|
|
16
|
+
└── DsarPolicyField
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- **`DsarPolicyPath`** is either a **root** (a top-level object the subject is identified on) or a
|
|
20
|
+
**relationship edge** from a parent object to a related object. Multiple roots are allowed — a
|
|
21
|
+
subject identified on both `Contact` and `Lead` gets one root path each.
|
|
22
|
+
- **`DsarPolicyField`** names a field to include in the export at its parent path.
|
|
23
|
+
- All three are **Metadata API** entities. `DsarPolicyLog` (run history) is **not** part of this
|
|
24
|
+
tree — it is standard SOQL (see `export-and-history.md`).
|
|
25
|
+
|
|
26
|
+
## Hard caps (the validator enforces these)
|
|
27
|
+
|
|
28
|
+
| Cap | Value |
|
|
29
|
+
|-----|-------|
|
|
30
|
+
| Children per path | **10** |
|
|
31
|
+
| Tree depth | **10** |
|
|
32
|
+
| Total nodes (paths) | **200** |
|
|
33
|
+
| Developer name | must match `[a-zA-Z]+[a-zA-Z0-9_]*` |
|
|
34
|
+
|
|
35
|
+
An unbounded request ("everything reachable from Account", "follow every relationship") will
|
|
36
|
+
exceed one of these. **Name the specific cap and stop** — do not start walking the object graph to
|
|
37
|
+
prove it (that times the turn out). Offer a bounded alternative: split into multiple policies, or
|
|
38
|
+
prune to the branches that actually hold the subject's data.
|
|
39
|
+
|
|
40
|
+
## Resolve names — never assume
|
|
41
|
+
|
|
42
|
+
Before authoring, resolve the real relationship and field API names against the org schema:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
sf sobject describe --sobject Contact --target-org <alias> --json # fields + child relationships
|
|
46
|
+
sf sobject describe --sobject Account --target-org <alias> --json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Use the `relationshipName`/`field` values from the describe output verbatim. A path that names a
|
|
50
|
+
relationship the org does not have will fail deploy.
|
|
51
|
+
|
|
52
|
+
## The `<tree.json>` shape for the validator
|
|
53
|
+
|
|
54
|
+
`scripts/validate-policy-tree.py` takes a JSON file describing the tree you intend to author. It
|
|
55
|
+
checks the caps and name regex **before** you write metadata, so a bad tree fails fast and cheap.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"developerName": "CustomerPortability",
|
|
60
|
+
"roots": [
|
|
61
|
+
{
|
|
62
|
+
"developerName": "ContactRoot",
|
|
63
|
+
"object": "Contact",
|
|
64
|
+
"fields": ["FirstName", "LastName", "Email"],
|
|
65
|
+
"children": [
|
|
66
|
+
{
|
|
67
|
+
"developerName": "ContactCases",
|
|
68
|
+
"relationship": "Cases",
|
|
69
|
+
"object": "Case",
|
|
70
|
+
"fields": ["Subject", "Description"],
|
|
71
|
+
"children": []
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `developerName` on every node → checked against `[a-zA-Z]+[a-zA-Z0-9_]*`.
|
|
80
|
+
- `children` nesting → depth check (root = depth 1).
|
|
81
|
+
- child count per node → 10-per-path check.
|
|
82
|
+
- total node count (roots + all descendants) → 200-node check.
|
|
83
|
+
|
|
84
|
+
The script exits non-zero and prints the first violation. Fix the tree and re-run until it exits 0,
|
|
85
|
+
then author metadata that mirrors it exactly.
|
|
86
|
+
|
|
87
|
+
## Lifecycle
|
|
88
|
+
|
|
89
|
+
- A policy is authored **INACTIVE**. It must be **ACTIVE** to execute (Workflow B).
|
|
90
|
+
- **INACTIVE** to **edit or delete**; **ACTIVE** to **execute**.
|
|
91
|
+
- To change an ACTIVE policy: **deactivate → edit → stop.** Never edit in place while ACTIVE, and
|
|
92
|
+
**never reactivate on your own** — report the edit and get **explicit user confirmation** before
|
|
93
|
+
reactivating/publishing. Auto-republishing breaks the reviewable audit trail that
|
|
94
|
+
portability/erasure disputes rely on.
|
|
95
|
+
- Authoring, editing, or deleting the *policy* **never** touches the data subject's records — it
|
|
96
|
+
only changes the export map. This is the export-not-deletion guardrail restated for configure.
|
|
97
|
+
|
|
98
|
+
## Deploy
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sf project deploy start --source-dir <outputDir> --target-org <alias> --json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Deploy can legitimately fail where `DsarPolicy`/sub-entities are not fully enabled on the org.
|
|
105
|
+
Surface the raw error and name the prerequisite; the classification strategy and validated tree are
|
|
106
|
+
still the deliverable and belong in the report regardless of deploy outcome.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Run an export & read history — reference
|
|
2
|
+
|
|
3
|
+
Deep detail for **Workflow B** (export) and **Workflow C** (history). Judgment (export-not-erasure,
|
|
4
|
+
consent, poll-before-download) lives in `SKILL.md`; this file is the mechanics.
|
|
5
|
+
|
|
6
|
+
## Resolve the DSR routes at run time
|
|
7
|
+
|
|
8
|
+
The execute / status / getfile routes are Connect DSR endpoints. Resolve them against the running
|
|
9
|
+
org — do not hardcode a version segment.
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
salesforce-api-context # MCP: resolve the current DSR execute/status/getfile routes + API version
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or discover via `sf`:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
sf api request rest '/services/data' --target-org <alias> # available API versions
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Resolve the subject (B0) — before anything else
|
|
22
|
+
|
|
23
|
+
A portability request names the subject by **email / name / external id**, not a `dataSubjectId`.
|
|
24
|
+
Resolve it to a record whose entity type is a **ROOT** of the policy you will run
|
|
25
|
+
(Account / Contact / Individual / Lead / User). Query the roots for the identifier, e.g.:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
sf data query --target-org <alias> \
|
|
29
|
+
--query "SELECT Id, Name, Email FROM Lead WHERE Email = '<subject-email>'"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Confirm the match is a **root type of the chosen policy** — execute matches only the root subtree of
|
|
33
|
+
the subject's type, so a subject whose type is not a policy root exports nothing. On **0 matches,
|
|
34
|
+
multiple matches, or a non-root type**, stop and report — never execute against a guessed Id.
|
|
35
|
+
|
|
36
|
+
## Pick the policy
|
|
37
|
+
|
|
38
|
+
If the request names a policy, use it; if not, use the policy established in the working context. If
|
|
39
|
+
none is established or **more than one ACTIVE policy could match the subject**, ask the user to
|
|
40
|
+
confirm which to run (surface the best match as a confirmation) before executing — never pick
|
|
41
|
+
silently.
|
|
42
|
+
|
|
43
|
+
## Preconditions
|
|
44
|
+
|
|
45
|
+
- Policy must be **ACTIVE** (INACTIVE cannot execute — route to Workflow A to activate).
|
|
46
|
+
- Run guards (name the missing one and stop on `401`/`403`; do not loop):
|
|
47
|
+
- user permission `Consent.CAN_EXECUTE_DSAR_POLICY`
|
|
48
|
+
- org feature `Consent.hasDsarPortability`
|
|
49
|
+
|
|
50
|
+
## Execute — capture the handle, trust the envelope
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
sf api request rest '<DSR_EXECUTE_ROUTE>' --method POST \
|
|
54
|
+
--body '{"policy":"<POLICY_DEVNAME>","subject":"<SUBJECT_IDENTIFIER>"}' \
|
|
55
|
+
--target-org <alias>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- Capture the **run handle** from the response — you need it for both status and getfile.
|
|
59
|
+
- **A failed run can return HTTP 201.** The real outcome is the **status field inside the
|
|
60
|
+
envelope**, not the HTTP code. Parse the body; if it reports failure, report failure.
|
|
61
|
+
- This is an **export**, not a deletion. It produces a file; it removes nothing. If the request
|
|
62
|
+
framed it as erasure, you have already corrected that (SKILL.md call #1) before reaching here.
|
|
63
|
+
|
|
64
|
+
Example failure envelope on a 201:
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{ "status": "FAILED", "message": "…", "runId": "…" } // HTTP 201, but FAILED — report the failure
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Poll a couple of times — then ask; download only after terminal
|
|
71
|
+
|
|
72
|
+
Poll the **same handle** (or `DsarPolicyLog.RequestStatus` for the run) for a terminal status
|
|
73
|
+
(`COMPLETED` / `FAILED` or the org's equivalents). Only a terminal-success run has a downloadable
|
|
74
|
+
file.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
sf api request rest '<DSR_STATUS_ROUTE_FOR_HANDLE>' --method GET --target-org <alias>
|
|
78
|
+
# or, by handle, over the run log:
|
|
79
|
+
sf data query --target-org <alias> \
|
|
80
|
+
--query "SELECT Id, RequestStatus, CompletionDatetime, DsarError FROM DsarPolicyLog WHERE Id = '<handle>'"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Poll a **small, fixed number of times** (≈2–3). If the run is still not terminal, **stop and ask the
|
|
84
|
+
user whether to keep polling** — do **not** loop to terminal. A run can sit non-terminal
|
|
85
|
+
indefinitely on downstream async processing; **that is platform / Tool Factory territory, not this
|
|
86
|
+
skill's to diagnose or reach into** (don't go hunting message-queue internals). Report the status in
|
|
87
|
+
plain terms — **running / completed / errored** — from the run row; the user does not need to know
|
|
88
|
+
which internal entity or queue backs it.
|
|
89
|
+
|
|
90
|
+
**Do not quote a completion time (e.g. "~1s") — even if a data source suggests one.** Runtime
|
|
91
|
+
varies by org provisioning; poll to terminal or hand off the handle. Never promise a duration.
|
|
92
|
+
|
|
93
|
+
An early *getfile* returns `NOT_FOUND` / `"This file isn't ready yet"`. **That is the contract
|
|
94
|
+
working, not a failure** — poll again; do not treat it as a completed or errored run.
|
|
95
|
+
|
|
96
|
+
## Download the export file — segment is `dsr`
|
|
97
|
+
|
|
98
|
+
Only after terminal-success:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sf api request rest '<DSR_GETFILE_ROUTE_FOR_HANDLE>' --method GET --target-org <alias>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- The getfile path segment is **`dsr`**, not `dsar`. A `dsar` segment 404s. Double-check the
|
|
105
|
+
segment before concluding the file is missing.
|
|
106
|
+
- Report where the export landed.
|
|
107
|
+
|
|
108
|
+
## Workflow C — history is a SOQL read
|
|
109
|
+
|
|
110
|
+
Run history lives in `DsarPolicyLog`, a standard object. Read it with SOQL — it is **not** an
|
|
111
|
+
`installListView` call, not a UI list, and it **never** starts a run.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
sf data query --target-org <alias> \
|
|
115
|
+
--query "SELECT Id, DsarPolicyId, RequestStatus, CreatedDate, CompletionDatetime \
|
|
116
|
+
FROM DsarPolicyLog \
|
|
117
|
+
WHERE DsarPolicyId = '<POLICY_ID>' \
|
|
118
|
+
ORDER BY CreatedDate DESC"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- Report each prior run (when it ran, its status). "No prior runs" is a valid, correct result.
|
|
122
|
+
- If `DsarPolicyLog` is absent or the query returns `401`/`403`/`404`, surface the raw error and
|
|
123
|
+
name the prerequisite — an accepted terminal outcome. Do not fall back to executing the policy.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Gap-analysis guide prompt — coverage audit for a DsarPolicy
|
|
2
|
+
|
|
3
|
+
> This is the guidance the agent follows when an admin asks *"what personal data are we missing
|
|
4
|
+
> from this policy?"*. It is the script for **Workflow D — Coverage gap analysis**: a **read-only**
|
|
5
|
+
> audit that surfaces **candidate** objects/fields for the admin to disposition — it never
|
|
6
|
+
> classifies data authoritatively, never adds a path on its own, and never activates a policy.
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [What this is and is not](#what-this-is-and-is-not)
|
|
11
|
+
- [The deterministic steps](#the-deterministic-steps)
|
|
12
|
+
- [How fields are flagged as candidate personal data (and why)](#how-fields-are-flagged-as-candidate-personal-data-and-why)
|
|
13
|
+
- [User-facing output (canonical)](#user-facing-output-canonical--use-verbatim-structure)
|
|
14
|
+
- [Guardrails](#guardrails)
|
|
15
|
+
|
|
16
|
+
## What this is and is not
|
|
17
|
+
|
|
18
|
+
The gap analysis answers **"which objects/fields that may hold this subject's personal data are
|
|
19
|
+
not yet covered by the policy?"** It is deliberately **structured and deterministic** — the same
|
|
20
|
+
request produces the same steps and the same transparency, not an open-ended graph walk that
|
|
21
|
+
varies run to run.
|
|
22
|
+
|
|
23
|
+
- **It is:** a read-only audit that (1) states its own method up front, (2) shows exactly which
|
|
24
|
+
objects it looked at, (3) flags **candidate** personal-data fields **with a reason for each**,
|
|
25
|
+
(4) states its search limit, and (5) hands the admin a disposition choice.
|
|
26
|
+
- **It is not:** an authoritative classification of what counts as personal data (that decision is
|
|
27
|
+
the **admin's** — load-bearing call #2), an automatic edit to the policy, or an activation. The
|
|
28
|
+
agent proposes; the admin disposes.
|
|
29
|
+
|
|
30
|
+
## Internal vs. user-facing (read before running)
|
|
31
|
+
|
|
32
|
+
This is the Workflow-D-specific application of **SKILL.md load-bearing call #8 (work silently — never
|
|
33
|
+
narrate the skill's internals)**. Workflow D has two layers; keep them apart in what you *say*:
|
|
34
|
+
|
|
35
|
+
- **Internal — never narrated to the user:** the step numbers and step names below, the `sf`/describe
|
|
36
|
+
mechanics in `gap-scan.md`, and every author-facing note about truncation, event-message limits,
|
|
37
|
+
token cost, or how the audit is "scored." These steer *how you work*; they are not status updates.
|
|
38
|
+
Do not say "running Workflow D, step 2", "describing the root to avoid truncation", or "this keeps
|
|
39
|
+
the run from scoring 0." Just do the work and present the result.
|
|
40
|
+
- **User-facing — the ONLY things the user sees:** the four blocks in
|
|
41
|
+
[User-facing output (canonical — use verbatim structure)](#user-facing-output-canonical--use-verbatim-structure)
|
|
42
|
+
— method preamble, findings, depth offer, disposition. Nothing else.
|
|
43
|
+
|
|
44
|
+
If the environment blocks the scan (feature off / policy unreadable), say so in one plain sentence
|
|
45
|
+
inside the findings block — not by narrating the mechanics that failed.
|
|
46
|
+
|
|
47
|
+
## The deterministic steps
|
|
48
|
+
|
|
49
|
+
Run these in order. Do not skip the method preamble (step 0) — transparency is the point. **These
|
|
50
|
+
step numbers/names are internal scaffolding — do the work, don't announce the scaffolding** (see
|
|
51
|
+
[Internal vs. user-facing](#internal-vs-user-facing-read-before-running)).
|
|
52
|
+
|
|
53
|
+
0. **Open with the plain-language method line.** Before scanning, tell the admin in **plain words**
|
|
54
|
+
how you'll answer and its limits (see the script). This is the one allowed "here's how I'll do
|
|
55
|
+
this" sentence — it must **not** name the workflow ("Workflow D", "coverage-gap audit"), announce
|
|
56
|
+
that you're about to read the guide/mechanics, or cite a rule/tool (call #8). State the approach,
|
|
57
|
+
then start.
|
|
58
|
+
1. **Read current coverage.** Load the policy's existing roots and paths (read-only). These are
|
|
59
|
+
already-covered objects — the audit reports gaps *relative to* this set.
|
|
60
|
+
2. **Enumerate candidate objects — one level only.** For each policy **root** object, describe it
|
|
61
|
+
and list its **immediately related** objects (exactly **one relationship hop** away). This
|
|
62
|
+
depth-1 default is a hard limit that keeps performance and token cost bounded — do **not**
|
|
63
|
+
recurse further without explicit confirmation (step 5).
|
|
64
|
+
3. **Flag candidate fields with reasoning.** On the root and each one-hop object, mark the fields
|
|
65
|
+
that **may** hold the subject's personal data, and record **why** for each (see the flagging
|
|
66
|
+
rubric). Candidates only — never "this *is* PII."
|
|
67
|
+
4. **Report transparently.** Present: the objects you scanned, the candidate fields with the
|
|
68
|
+
per-field reason, the fields already covered vs newly surfaced, and an explicit statement of the
|
|
69
|
+
**one-level search limit**.
|
|
70
|
+
5. **Offer to go deeper — only on explicit confirmation.** Ask whether the admin wants to look
|
|
71
|
+
beyond one level. Warn plainly that going deeper produces **a large volume of output and burns
|
|
72
|
+
significantly more tokens**. Expand only if they say yes, and only by the depth they approve.
|
|
73
|
+
6. **Disposition, don't mutate.** Ask which candidates the admin wants to add. Nothing is added
|
|
74
|
+
automatically. Adding routes to **Workflow A (Configure)**, which authors the change INACTIVE
|
|
75
|
+
and — per the activation rule — **stops for explicit user confirmation before reactivating**.
|
|
76
|
+
|
|
77
|
+
## How fields are flagged as candidate personal data (and why)
|
|
78
|
+
|
|
79
|
+
The reason string is mandatory — every flagged field says *why*. Use these signals, strongest
|
|
80
|
+
first, and name the signal you used:
|
|
81
|
+
|
|
82
|
+
- **Compliance metadata on the field** (e.g. a field's data-sensitivity / compliance-group /
|
|
83
|
+
PII classification from the describe). Reason: *"marked <classification> in field metadata."*
|
|
84
|
+
This is the strongest signal and the least subjective.
|
|
85
|
+
- **Field type semantics** — `email`, `phone`, `address` compound components, `date` used for
|
|
86
|
+
birthdate. Reason: *"<type> field, commonly personal contact/identity data."*
|
|
87
|
+
- **Name/label semantics** — tokens like Name, First/Last, Email, Phone, Mobile, SSN, Passport,
|
|
88
|
+
DOB/Birth, Address/Street/City/Postal, IP, DeviceId, TaxId. Reason: *"name/label suggests
|
|
89
|
+
<category>."*
|
|
90
|
+
- **Already-covered elsewhere** — the same value appears on an already-mapped object. Reason:
|
|
91
|
+
*"duplicate of a field already in the policy; flagged for the admin to decide canonical source."*
|
|
92
|
+
|
|
93
|
+
State the rubric's limits honestly: this is a **heuristic**, not a legal determination. A field
|
|
94
|
+
the rubric misses can still be personal data, and a flagged field may not be — the admin confirms.
|
|
95
|
+
When compliance metadata is absent on the org, say so (the audit leans on name/type semantics only).
|
|
96
|
+
|
|
97
|
+
## User-facing output (canonical — use verbatim structure)
|
|
98
|
+
|
|
99
|
+
**This is the output contract for Workflow D — the four blocks below are the only thing the user
|
|
100
|
+
sees, and their structure is fixed.** Use these four blocks, in this order, with this formatting
|
|
101
|
+
(the blockquote framing and the `Object | Field | Why I flagged it | In policy?` table columns);
|
|
102
|
+
do not improvise a different layout run to run. The *prose wording* may be adapted to the specific
|
|
103
|
+
policy/org, but the **blocks, their order, and the table columns are not optional** — stable
|
|
104
|
+
structure is what keeps the presentation clean instead of randomly formatted. Do not surface any of
|
|
105
|
+
the internal step numbers, mechanics, or scoring/truncation notes here (see
|
|
106
|
+
[Internal vs. user-facing](#internal-vs-user-facing-read-before-running)).
|
|
107
|
+
|
|
108
|
+
**Method preamble (step 0):**
|
|
109
|
+
|
|
110
|
+
> Here's how I can answer this. I'll start from your policy's root objects, look **one level out**
|
|
111
|
+
> at the objects directly related to them, and flag the fields that *might* contain this person's
|
|
112
|
+
> data — and I'll tell you **why** I flagged each one. I won't decide what counts as personal data
|
|
113
|
+
> for you, and I won't change the policy: you'll pick what to add. I stop at one level by default;
|
|
114
|
+
> I can go deeper if you ask, but that gets large and token-heavy, so I'll check with you first.
|
|
115
|
+
|
|
116
|
+
**Findings (step 4):**
|
|
117
|
+
|
|
118
|
+
> **Objects I looked at:** `<root objects>` and their immediately-related objects: `<one-hop list>`.
|
|
119
|
+
> **Already covered by the policy:** `<covered fields>`.
|
|
120
|
+
> **Candidate fields that may hold personal data (not yet covered):**
|
|
121
|
+
> | Object | Field | Why I flagged it | In policy? |
|
|
122
|
+
> |--------|-------|------------------|------------|
|
|
123
|
+
> | Contact | Email | email field, common contact PII | no |
|
|
124
|
+
> | Contact | Birthdate | date used as date of birth (name/type) | no |
|
|
125
|
+
> **Search limit:** I only looked **one level** out from your roots. Deeper relationships are not
|
|
126
|
+
> in this list.
|
|
127
|
+
|
|
128
|
+
**Depth gate (step 5):**
|
|
129
|
+
|
|
130
|
+
> Want me to look **beyond one level**? Heads up: it produces a lot more output and uses
|
|
131
|
+
> considerably more tokens. If yes, tell me how deep (e.g. one more level) and I'll continue.
|
|
132
|
+
|
|
133
|
+
**Disposition (step 6):**
|
|
134
|
+
|
|
135
|
+
> Which of these do you want added to the policy? I'll author the change and leave it **inactive**
|
|
136
|
+
> for your review — I won't reactivate or publish until you confirm.
|
|
137
|
+
|
|
138
|
+
## Guardrails
|
|
139
|
+
|
|
140
|
+
- **Candidates, never verdicts.** Every flag is a suggestion pending the admin's disposition.
|
|
141
|
+
- **Depth 1 by default.** Never recurse past one level without explicit, warned confirmation.
|
|
142
|
+
- **Read-only.** The audit changes nothing; adding is a separate, admin-driven Workflow A step.
|
|
143
|
+
- **No auto-activation.** Any resulting edit stops for explicit user confirmation before
|
|
144
|
+
reactivation/publish (the activation rule).
|
|
145
|
+
- **Transparency is mandatory.** Always report what was scanned, why each field was flagged, and
|
|
146
|
+
the search limit — even when the list is short or empty.
|
|
147
|
+
- **Transparency ≠ narrating internals.** Show *what* you scanned and *why* each field is flagged
|
|
148
|
+
(the findings block). Do **not** narrate *how* — step numbers, describe mechanics, truncation /
|
|
149
|
+
token / scoring notes stay internal. Transparency is about the audit's coverage and reasoning, not
|
|
150
|
+
the skill's plumbing.
|