platform-datamask-run

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

platform-datamask-run: Salesforce Data Mask End-to-End Operation

platform-datamask-run: Salesforce Data Mask端到端操作

Use this skill to operate the Salesforce Data Mask feature on a sandbox: configure a masking policy over PII fields, start a masking job, poll it to a terminal state, report which records were masked, and abort a run that is still in progress.
Data Mask is sandbox-only — the run/abort REST endpoints return
403
on production (a runtime sandbox guard). Confirm the target org is a sandbox before starting.
本技能用于在沙盒环境操作Salesforce Data Mask功能:针对PII字段配置掩码策略、启动掩码任务、轮询任务直至进入终端状态、报告已掩码的记录,以及中止仍在进行中的任务。
Data Mask仅支持沙盒环境——在生产环境中调用运行/中止REST端点会返回
403
(运行时沙盒防护机制)。启动任务前请确认目标组织为沙盒环境。

When This Skill Owns the Task

本技能负责的任务场景

  • Running a Data Mask job against a configured policy
  • Polling masking-job status to completion
  • Reporting masked-record counts / per-object results
  • Aborting (canceling) an in-progress masking run
  • Creating or identifying the policy the job runs against
Delegate elsewhere when the user is:
  • hand-writing anonymization Apex →
    platform-apex-generate
  • seeding or generating test data →
    platform-data-manage
  • deploying unrelated metadata →
    platform-metadata-deploy

  • 针对已配置的策略运行Data Mask任务
  • 轮询掩码任务状态直至完成
  • 报告已掩码记录数量 / 按对象统计的结果
  • 中止(取消)正在进行的掩码任务
  • 创建或识别任务所使用的策略
当用户进行以下操作时,请委托至其他技能:
  • 手动编写匿名化Apex代码 →
    platform-apex-generate
  • 植入或生成测试数据 →
    platform-data-manage
  • 部署无关元数据 →
    platform-metadata-deploy

The One Thing to Get Right First: the API surface map

首要注意事项:API接口映射

The single biggest failure mode is assuming Data Mask entities are ordinary data-API objects. They are not, and the surface differs per entity. Memorize this table before running anything — guessing here is what turns a 3-second job into a 30-minute dead end.
EntityWhat it isHow you reach it
DataMaskPolicy
The masking policy shell (config)Tooling API or Metadata API (thin shell:
<label>
/
<description>
/
<runOnRefresh>
only) — NOT standard SOQL/
sobject describe
DataMaskPolicyObject
An object targeted by a policy (holds the optional row filter)Tooling API only — query AND insert; row-subset "sample" runs set
FilterEnabled
+
WhereCriteria
here (no
sampleSize
on the policy)
DataMaskPolicyField
A field + its masking treatmentTooling API only — query AND insert; treatment cols are
MaskingCategory
+
MaskValue
DataMaskPolicyJobRun
The job (one masking run)Standard SOQL
sf data query
works
DataMaskPolicyJobRunDtl
Per-object job detail (child, FK
DataMaskPolicyJobRunId
)
Standard SOQL
Start a runREST run API
POST /services/data/v67.0/platform/data-resilience/data-mask/policies/{policyId}/run
Abort a runREST run API
POST /services/data/v67.0/platform/data-resilience/data-mask/jobs/{jobRunId}/abort
Concretely:
  • sf sobject describe --sobject DataMaskPolicy
    NOT_FOUND
    (don't retry it against standard API)
  • SELECT ... FROM DataMaskPolicy
    via
    sf data query
    INVALID_TYPE
  • Query the policy via Tooling:
    sf data query --use-tooling-api --query "SELECT Id, MasterLabel FROM DataMaskPolicy"
  • Query the job / job-detail via standard API:
    sf data query --query "SELECT Id, Status FROM DataMaskPolicyJobRun"
Full command reference:
references/api-surface.md
.

最常见的失败原因是误以为Data Mask实体是普通的数据API对象。事实并非如此,且不同实体的接口存在差异。运行任何操作前请牢记下表——错误假设会让3秒就能完成的任务变成30分钟的死胡同。
Entity类型说明访问方式
DataMaskPolicy
掩码策略外壳(配置)Tooling APIMetadata API(仅包含
<label>
/
<description>
/
<runOnRefresh>
的轻量外壳)—— 不支持标准SOQL/
sobject describe
DataMaskPolicyObject
策略目标对象(包含可选行过滤器)仅支持Tooling API —— 支持查询和插入;行子集“采样”运行需在此设置
FilterEnabled
+
WhereCriteria
(策略本身无
sampleSize
参数)
DataMaskPolicyField
字段及其掩码处理规则仅支持Tooling API —— 支持查询和插入;处理规则列包括
MaskingCategory
+
MaskValue
DataMaskPolicyJobRun
任务实例(单次掩码运行)标准SOQL ——
sf data query
可正常使用
DataMaskPolicyJobRunDtl
按对象统计的任务详情(子对象,外键
DataMaskPolicyJobRunId
标准SOQL
启动任务REST运行API
POST /services/data/v67.0/platform/data-resilience/data-mask/policies/{policyId}/run
中止任务REST运行API
POST /services/data/v67.0/platform/data-resilience/data-mask/jobs/{jobRunId}/abort
具体示例:
  • sf sobject describe --sobject DataMaskPolicy
    NOT_FOUND
    (请勿在标准API中重试)
  • 通过
    sf data query
    执行
    SELECT ... FROM DataMaskPolicy
    INVALID_TYPE
  • 通过Tooling API查询策略
    sf data query --use-tooling-api --query "SELECT Id, MasterLabel FROM DataMaskPolicy"
  • 通过标准API查询任务/任务详情
    sf data query --query "SELECT Id, Status FROM DataMaskPolicyJobRun"
完整命令参考:
references/api-surface.md

Pick the workflow that matches the request

根据需求选择对应工作流

This skill has two distinct workflows. Select ONE up front from what the user asked for, then run every step of that workflow — neither has optional steps:
The user wants to…RunEnds when
Configure/edit a policy and mask records; report how many were maskedWorkflow A — Mask & report (below)The masked count is reported from the detail rows
Cancel / abort a masking runWorkflow B — Cancel a run (further below)The job's status is confirmed
canceled
Choose by the verb in the request. "Create/edit a policy and run it", "mask the PII", "how many records were masked" → Workflow A only. "Abort", "cancel", "stop the run" → Workflow B. A mask-and-report request does not include an abort: do not start a second job to "demonstrate" cancelling — an unrequested run wastes a full ~5–10 min job (see the pool floor in A4) and is the top cause of this task running out of turn before it finishes the masked count it was asked for.

本技能包含两种独立工作流。根据用户需求预先选择其中一种,然后执行该工作流的所有步骤——两种工作流均无可选步骤:
用户需求运行工作流结束标志
配置/编辑策略并掩码记录;报告已掩码的记录数量工作流A — 掩码并报告(如下)从详情行中报告已掩码记录数量
取消/中止掩码任务工作流B — 取消任务(如下)确认任务状态为
canceled
根据请求中的动词进行选择。“创建/编辑策略并运行”、“掩码PII”、“已掩码多少条记录” → 仅使用工作流A。“中止”、“取消”、“停止任务” → 使用工作流B。掩码并报告的请求不包含中止操作:请勿启动第二个任务来“演示”取消——未被请求的任务会浪费约5–10分钟的任务时间(见A4中的池底延迟),也是导致任务在完成已请求的掩码数量前超时的最主要原因。

Workflow A — Mask & report

工作流A — 掩码并报告

A1. Confirm sandbox + capture org context

A1. 确认沙盒环境并捕获组织上下文

Verify the org is a sandbox and grab the instance URL + a session token for the run-API calls:
bash
sf org display --target-org <alias> --json
验证目标组织为沙盒环境,并获取实例URL和会话令牌用于调用运行API:
bash
sf org display --target-org <alias> --json

A2. Identify or create the policy

A2. 识别或创建策略

Prefer reusing an existing policy (fastest, no deploy):
bash
sf data query --use-tooling-api --target-org <alias> \
  --query "SELECT Id, DeveloperName, MasterLabel FROM DataMaskPolicy"
If none targets the Contact PII you need, author one with the two-step recipe (the
DataMaskPolicy
Metadata shape is a thin shell; membership is Tooling-inserted):
  1. Metadata-deploy the thin shell in mdapi format (
    --metadata-dir
    +
    package.xml
    ; a source-format
    --source-dir
    deploy fails "Could not infer a metadata type"). The shell carries only
    <label>
    ,
    <description>
    ,
    <runOnRefresh>
    . This creates the policy with an active revision, which A2 requires.
  2. Tooling-insert the
    DataMaskPolicyObject
    (one per object) then its
    DataMaskPolicyField
    rows. Each field row's treatment is
    MaskingCategory
    (
    library
    ) +
    MaskValue
    (a snake_case token like
    first_name
    ,
    email
    ,
    phone
    ). There is no
    MaskingRuleType
    column
    .
Insert order matters: a Tooling-created parent (no active revision) makes the child insert fail
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY
. Metadata-deploy the shell first.
See
references/policy-authoring.md
for the full recipe and the
MaskValue
token table. Choose a
MaskValue
appropriate to each field; do not blanket-replace.
优先复用现有策略(最快,无需部署):
bash
sf data query --use-tooling-api --target-org <alias> \
  --query "SELECT Id, DeveloperName, MasterLabel FROM DataMaskPolicy"
如果没有策略针对你需要处理的Contact PII数据,请按照两步法创建策略(
DataMaskPolicy
元数据结构为轻量外壳;成员需通过Tooling API插入):
  1. 以mdapi格式元数据部署轻量外壳(使用
    --metadata-dir
    +
    package.xml
    ;使用source格式的
    --source-dir
    部署会失败,提示"Could not infer a metadata type")。外壳仅包含
    <label>
    <description>
    <runOnRefresh>
    。此步骤会创建带有活动版本的策略,这是A2的必要条件。
  2. 通过Tooling API插入
    DataMaskPolicyObject
    (每个对象对应一条记录),然后插入
    DataMaskPolicyField
    记录。每条字段记录的处理规则为
    MaskingCategory
    library
    ) +
    MaskValue
    (类似
    first_name
    email
    phone
    的蛇形命名令牌)。不存在
    MaskingRuleType
插入顺序至关重要:通过Tooling API创建的父对象(无活动版本)会导致子对象插入失败,提示
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY
。请先部署元数据外壳。
完整创建指南和
MaskValue
令牌表请参考
references/policy-authoring.md
。为每个字段选择合适的
MaskValue
;请勿统一替换。

A3. Start the masking run (REST run API)

A3. 启动掩码任务(REST运行API)

bash
printf '{}' > ./empty-body.json
sf api request rest \
  "/services/data/v67.0/platform/data-resilience/data-mask/policies/{policyId}/run" \
  --method POST --body @./empty-body.json --target-org <alias>
The endpoint needs an empty JSON body (
{}
) —
sf api request rest
requires
--body
on a POST even when the API takes no payload. Pass the file with an
@
prefix
(
--body @./empty-body.json
); without it the literal path is sent as the body and the API returns
JSON_PARSER_ERROR
. A
200
returns
jobRunId
,
policyId
,
status
(the run-API status is UPPERCASE, e.g.
RUNNING
) and
message: "Job started successfully"
. A
409
/
CONFLICT
means a run is already in progress for that policy.
Write
report.md
NOW, before you poll — do not wait until the end.
The masking job takes several minutes (see below), and the single most common way this task scores zero is the turn ending during the poll with no output file written at all. The instant you have the
jobRunId
, write
report.md
with everything known so far (policy Id/label, the run command, the
jobRunId
, status
RUNNING
, and a "polling for completion…" placeholder for the masked count). Then update that same file once the job finishes. A report that exists and says "still running" beats no file; a fabricated count is worse than either — only fill the count from the detail rows (A5).
bash
printf '{}' > ./empty-body.json
sf api request rest \
  "/services/data/v67.0/platform/data-resilience/data-mask/policies/{policyId}/run" \
  --method POST --body @./empty-body.json --target-org <alias>
该端点需要空JSON请求体
{}
)——即使API不需要负载,
sf api request rest
在POST请求时仍需指定
--body
使用
@
前缀传递文件
--body @./empty-body.json
);如果不添加
@
,字面路径会被作为请求体发送,API会返回
JSON_PARSER_ERROR
。返回
200
状态码时会包含
jobRunId
policyId
status
(运行API返回的状态为大写,例如
RUNNING
)以及
message: "Job started successfully"
。返回
409
/
CONFLICT
表示该策略已有任务在运行中。
立即编写
report.md
,不要等到轮询结束
。掩码任务需要数分钟时间(如下所述),导致任务得分为零的最常见原因是轮询期间任务超时,且未生成任何输出文件。一旦获取到
jobRunId
,立即编写
report.md
,填入当前已知的所有信息(策略ID/标签、运行命令、
jobRunId
、状态
RUNNING
,以及用于填写掩码数量的“轮询中……”占位符)。然后在任务完成后更新该文件。一份存在且显示“运行中”的报告比没有报告要好;编造的数量比这两种情况都糟——仅从A5的详情行中获取掩码数量。

A4. Poll to a terminal state (standard SOQL)

A4. 轮询直至进入终端状态(标准SOQL)

Poll
DataMaskPolicyJobRun.Status
until it reaches a terminal value. Do not report a mid-run status as final.
  • Mid-run (pre-work):
    pending
    ,
    scheduled
    — the job is queued but not yet abortable
  • Mid-run (working):
    running
    — this is the only state in which abort succeeds
  • Terminal:
    completed
    ,
    completed_with_errors
    ,
    failed
  • Abort target:
    canceled
    (single "l")
pending
is not
running
.
Abort on a
pending
/
scheduled
job returns
409 CONFLICT
("Job is not in a running state ... status=PENDING"). You must poll until the status is literally
running
before you can abort — see Workflow B.
Jobs are slow — expect several minutes, and poll with the bundled script. Data Mask runs on a backend pool/scheduler with a ~5–10 minute floor: even a tiny (20-row) job usually does not reach a terminal state or emit detail rows for several minutes after the run starts. This is fixed overhead, not proportional to row count. Plan the run around it — the single biggest failure mode is treating the job as instant, polling on a tight interval, and either timing out or writing a "still pending" report.
Run
scripts/poll-job.sh
as a single command — do not hand-roll a SOQL poll loop:
bash
bash scripts/poll-job.sh <alias> <jobRunId>        # defaults: cap 600s (10 min), 20s interval
It sleeps on a low-frequency interval, short-circuits the instant a ground-truth detail row appears, prints the terminal signal (
completed
/
failed
/
canceled
) on stdout, and exits
0
(or
1
on timeout). Call it once and read its result — do not wrap it in your own retry loop, and do not poll on a sub-10s interval (it just burns tool calls against a job that cannot finish sooner).
Ground truth is the detail rows, not the parent status. The parent
DataMaskPolicyJobRun.Status
can lag — it may read
pending
/
running
for a while after masking actually finished. Once a
total_records_masked
(or
completed
)
DataMaskPolicyJobRunDtl
row exists, the masking is done.
poll-job.sh
already encodes all of this — the bounded interval and timeout, the short-circuit on the ground-truth detail row, and the terminal-signal exit code — so you do not re-implement any of it inline. Run the poller once, read its exit signal, then update
report.md
(the stub you wrote before polling) with the terminal status and the masked count from A5.
轮询
DataMaskPolicyJobRun.Status
直至其变为终端值。请勿将运行中的状态报告为最终状态。
  • 运行前(待处理):
    pending
    scheduled
    ——任务已排队但尚未可中止
  • 运行中:
    running
    ——这是唯一可成功中止的状态
  • 终端状态:
    completed
    completed_with_errors
    failed
  • 中止目标状态:
    canceled
    (单个"l")
pending
不等于
running
。对
pending
/
scheduled
状态的任务执行中止操作会返回
409 CONFLICT
("Job is not in a running state ... status=PENDING")。必须轮询至状态变为
running
后才能执行中止——详见工作流B。
任务运行缓慢——预计需要数分钟,请使用捆绑脚本进行轮询。Data Mask运行在后端池/调度器上,存在约5–10分钟的基础延迟:即使是小型(20行)任务,通常在启动后数分钟内也不会进入终端状态或生成详情行。这是固定开销,与行数无关。请据此规划任务——最常见的失败原因是将任务视为即时完成,使用过短的间隔轮询,导致超时或生成“仍待处理”的报告。
运行
scripts/poll-job.sh
作为单个命令——请勿手动编写SOQL轮询循环:
bash
bash scripts/poll-job.sh <alias> <jobRunId>        # 默认设置:最长等待600秒(10分钟),间隔20秒
该脚本会以低频率间隔休眠,一旦出现真实的详情行就会立即终止,在标准输出中打印终端信号(
completed
/
failed
/
canceled
),并返回
0
(超时则返回
1
)。调用一次并读取结果——请勿将其包裹在自定义重试循环中,也不要使用小于10秒的间隔轮询(这只会浪费工具调用次数,而任务无法更快完成)。
真实状态以详情行为准,而非父任务状态。父任务
DataMaskPolicyJobRun.Status
可能存在延迟——掩码实际完成后,它可能仍显示
pending
/
running
状态一段时间。一旦出现包含
total_records_masked
(或
completed
)的
DataMaskPolicyJobRunDtl
记录,掩码即完成。
poll-job.sh
已包含所有逻辑——有限的等待时间和超时机制、基于真实详情行的终止条件、终端信号退出码——因此你无需手动实现任何逻辑。运行一次轮询脚本,读取其退出信号,然后更新之前编写的
report.md
(轮询前的草稿),填入终端状态和A5中的掩码数量。

A5. Report results from the job DETAIL object

A5. 从任务详情对象报告结果

The parent job carries an overall status; per-object masked counts live on the child
DataMaskPolicyJobRunDtl
(linked by
DataMaskPolicyJobRunId
). Report a concrete count, not a fabricated one:
bash
sf data query --target-org <alias> \
  --query "SELECT Id, DataMaskPolicyJobRunId, Status FROM DataMaskPolicyJobRunDtl WHERE DataMaskPolicyJobRunId = '<jobRunId>'"
Report only what the rows literally show — do not overstate granularity. The detail rows are object-level status_update entries (
loaded
,
completed
,
total_records_masked
for the object, e.g. Contact). They are not per-field rows. So state per-object success as an observed fact ("Contact: 27/27 records masked, 0 error rows"), but frame field-level success as an inference, not a direct observation — say "no field-level error rows were returned, so no field is reported as failed", not "all 5 fields succeeded" (the data does not carry a per-field success row to back that claim). Overstating an inference as an observation is the most common factuality miss here.

父任务包含整体状态;按对象统计的掩码数量存储在子对象
DataMaskPolicyJobRunDtl
中(通过
DataMaskPolicyJobRunId
关联)。报告真实的统计数量,请勿编造:
bash
sf data query --target-org <alias> \
  --query "SELECT Id, DataMaskPolicyJobRunId, Status FROM DataMaskPolicyJobRunDtl WHERE DataMaskPolicyJobRunId = '<jobRunId>'"
仅报告记录中明确显示的内容——不要夸大粒度。详情行是对象级的状态更新记录(
loaded
completed
、对象的
total_records_masked
,例如Contact)。它们不是按字段统计的记录。因此,将对象级成功作为观察事实进行陈述("Contact:27/27条记录已掩码,0条错误行"),但将字段级成功作为推断而非直接观察结果——例如说"未返回字段级错误行,因此无字段被报告为失败",不要说"所有5个字段均成功"(数据中没有按字段统计的成功记录来支持这一说法)。将推断夸大视为观察结果是此处最常见的事实错误。

Workflow B — Cancel a run

工作流B — 取消任务

Use this workflow when the request is to abort/cancel a masking run. It targets the run that is currently in progress — aborting is an on-demand action against a live job; nobody starts a job just to cancel it. Steps B1–B4 are all required.
当用户请求中止/取消掩码任务时使用本工作流。它针对当前正在运行的任务——中止是针对活跃任务的按需操作;没有人会为了取消而启动任务。步骤B1–B4均为必填步骤。

B1. Confirm sandbox + identify the run to cancel

B1. 确认沙盒环境并识别要取消的任务

Confirm the org is a sandbox (
sf org display
) and get the
jobRunId
of the run to abort — the one the user is asking to cancel. Capture its
DataMaskPolicyId
too
— you need it to start a replacement run if the abort window is missed (B2 exit 3 / exit 1). If they just started it, use that id; otherwise query for the active run:
bash
sf data query --target-org <alias> \
  --query "SELECT Id, Status, DataMaskPolicyId FROM DataMaskPolicyJobRun ORDER BY CreatedDate DESC LIMIT 5"
Note the
DataMaskPolicyId
(
8dm
prefix) of the run you pick — that is the
<policyId>
A3 needs.
确认目标组织为沙盒环境(
sf org display
),并获取要中止的任务的
jobRunId
——即用户请求取消的任务。同时捕获其
DataMaskPolicyId
——如果错过中止窗口(B2的退出码3/1),你需要用它启动替换任务。如果用户刚刚启动任务,使用该ID;否则查询活跃任务:
bash
sf data query --target-org <alias> \
  --query "SELECT Id, Status, DataMaskPolicyId FROM DataMaskPolicyJobRun ORDER BY CreatedDate DESC LIMIT 5"
记录你选择的任务的
DataMaskPolicyId
(前缀为
8dm
)——这是A3中需要的
<policyId>

B2. Wait for the job to be
running
(the only abortable state)

B2. 等待任务变为
running
状态(唯一可中止的状态)

You can only abort while
DataMaskPolicyJobRun.Status
is
running
. A
pending
/
scheduled
job
409
s; a terminal one is already done. Poll for the
running
window with the bundled poller in its
running
mode
— it exits the instant the status reads
running
(unlike the default mode, which waits for a terminal state), so it will not block past the abortable window:
bash
POLL_MODE=running bash scripts/poll-job.sh <alias> <jobRunId> 900 15
The cap is 900s (15 min), above the ~5–10 min scheduling floor so a slow-to-start job still gets caught. Handle every exit:
  • Exit
    0
    (prints
    running
    ) → go straight to B3.
  • Exit
    3
    → the job raced to a terminal state before
    running
    was caught; the abort window is gone. Start a fresh run against the policy you captured in B1 (A3 with that
    <policyId>
    ), then return here and poll the new
    jobRunId
    .
  • Exit
    1
    (timeout — the cap expired) → re-query the job's status:
    bash
    sf data query --target-org <alias> \
      --query "SELECT Id, Status FROM DataMaskPolicyJobRun WHERE Id = '<jobRunId>'"
    If it is still non-terminal (
    pending
    /
    scheduled
    /
    running
    ), re-run the poller once more (same command) to continue waiting. If it is
    running
    , go to B3. If it is terminal, treat it like exit 3 — start a fresh run (A3 with the B1
    <policyId>
    ) and poll the new job.
Because of the ~5–10 min pool floor the
running
window is usually minutes wide, so there is time to catch it; do not poll with no delay.
If no run is currently in progress (the job already completed, or you must reproduce a run→cancel flow end to end), start one first with A3, then return here — poll it to
running
and abort that live job. Never substitute an older, already-terminal job to "show" a cancel; the abort must target the run that is actually live.
仅当
DataMaskPolicyJobRun.Status
running
时才能中止任务。对
pending
/
scheduled
状态的任务执行中止会返回
409
;终端状态的任务已完成。使用捆绑的轮询脚本的**
running
模式**轮询
running
窗口——一旦状态变为
running
就会立即退出(与默认模式等待终端状态不同),因此不会在中止窗口过后继续阻塞:
bash
POLL_MODE=running bash scripts/poll-job.sh <alias> <jobRunId> 900 15
最长等待时间为900秒(15分钟),高于约5–10分钟的调度基础延迟,因此即使启动缓慢的任务也能被捕获。处理所有退出情况:
  • 退出码
    0
    (打印
    running
    )→ 直接进入B3。
  • 退出码
    3
    → 任务在捕获到
    running
    状态前已进入终端状态;中止窗口已关闭。针对B1中捕获的策略启动新任务(使用该
    <policyId>
    执行A3),然后返回此处轮询
    jobRunId
  • 退出码
    1
    (超时——最长等待时间已到)→ 重新查询任务状态:
    bash
    sf data query --target-org <alias> \
      --query "SELECT Id, Status FROM DataMaskPolicyJobRun WHERE Id = '<jobRunId>'"
    如果状态仍为非终端(
    pending
    /
    scheduled
    /
    running
    ),再次运行一次轮询脚本(相同命令)继续等待。如果状态为
    running
    ,进入B3。如果状态为终端,按照退出码3处理——启动新任务(使用B1的
    <policyId>
    执行A3)并轮询新任务。
由于约5–10分钟的基础延迟,
running
窗口通常有数分钟宽,因此有足够时间捕获;请勿无延迟轮询。
如果当前没有任务在运行(任务已完成,或者你必须完整复现运行→取消流程),先通过A3启动任务,然后返回此处——轮询至
running
状态并中止该活跃任务。切勿使用已终止的旧任务来“演示”取消;中止操作必须针对实际活跃的任务。

B3. Abort via the run API

B3. 通过运行API中止任务

Abort via the run API — not by DML/delete on the job record:
bash
sf api request rest \
  "/services/data/v67.0/platform/data-resilience/data-mask/jobs/{jobRunId}/abort" \
  --method POST --body @./empty-body.json --target-org <alias>
Empty JSON body (
{}
) via the
@
-prefixed file, as above. A
200
returns
status: "CANCELED"
(uppercase, from the run API) and
message: "Job abort requested"
. A
409
means the job was not in a
running
state (usually still
pending
/
scheduled
) — return to B2 and resume polling.
通过运行API中止任务——不要通过DML/删除任务记录来中止:
bash
sf api request rest \
  "/services/data/v67.0/platform/data-resilience/data-mask/jobs/{jobRunId}/abort" \
  --method POST --body @./empty-body.json --target-org <alias>
使用
@
前缀的文件传递空JSON请求体(
{}
),如上所述。返回
200
状态码时会包含
status: "CANCELED"
(运行API返回的状态为大写)以及
message: "Job abort requested"
。返回
409
表示任务未处于
running
状态(通常为
pending
/
scheduled
)——返回B2继续轮询。

B4. Confirm and report the cancellation

B4. 确认并报告取消结果

Cancellation is asynchronous. Re-query
DataMaskPolicyJobRun
and confirm
Status = canceled
(lowercase, from SOQL) before reporting the abort succeeded. Verify:
  • Confirmed the abort targeted the live job while its queried status was
    running
    .
  • Re-queried
    DataMaskPolicyJobRun
    after the abort and saw
    Status = canceled
    .

取消操作是异步的。重新查询
DataMaskPolicyJobRun
并确认
Status = canceled
(SOQL返回的状态为小写)后,再报告中止成功。验证以下内容:
  • 确认中止操作针对的是查询状态为
    running
    的活跃任务。
  • 中止后重新查询
    DataMaskPolicyJobRun
    ,确认状态为
    canceled

High-Signal Rules

高优先级规则

RuleRationale
Run each
sf
command bare — never add a pipe or redirect of any kind (
|
,
| python3
,
| grep
,
2>&1
,
2>/dev/null
,
> file
)
sf ... --json
already prints clean JSON on stdout; read it directly. A redirect/pipe trips an unbypassable shell-safety guard that silently stalls the whole run to timeout. Never post-process with
python3
/
grep
/
jq
, and never suppress stderr — even if a command prints a warning, the
--json
payload on stdout is still valid; just parse it as-is
Never use standard SOQL /
sobject describe
on
DataMaskPolicy*
config objects
They return
INVALID_TYPE
/
NOT_FOUND
— use Tooling API or MDAPI
Read masked counts from
DataMaskPolicyJobRunDtl
, never invent them
The child detail is the source of truth for per-object results
Only
completed
/
completed_with_errors
/
failed
are terminal
Reporting
running
/
scheduled
as final is wrong
Abort only via the run-API abort endpointDML/delete on the job record is not a real abort and corrupts state
Always re-query status after abort and confirm
canceled
An abort call returning 200 is not proof the job stopped
Data Mask runs on sandboxes onlyRun/abort endpoints
403
on production
Use API version
v67.0
or later, and no
/connect/
segment
The run/abort endpoints are
/services/data/v67.0/platform/data-resilience/data-mask/...
— a
connect
segment or a pre-v67 version returns
NOT_FOUND
Poll via
scripts/poll-job.sh
(one call), never a hand-rolled SOQL loop
The script caps attempts and short-circuits on the ground-truth detail row; a manual loop against the lagging parent status is the #1 cause of a run timing out with no report

规则理由
运行每个
sf
命令时直接运行——不要添加任何管道或重定向(
|
| python3
| grep
2>&1
2>/dev/null
> file
sf ... --json
已在标准输出中打印清晰的JSON;直接读取即可。重定向/管道会触发无法绕过的shell安全防护,导致整个任务静默超时。切勿使用
python3
/
grep
/
jq
进行后处理,也不要抑制标准错误输出——即使命令打印警告,标准输出中的
--json
payload仍然有效;直接解析即可
切勿对
DataMaskPolicy*
配置对象使用标准SOQL /
sobject describe
它们会返回
INVALID_TYPE
/
NOT_FOUND
——请使用Tooling API或MDAPI
DataMaskPolicyJobRunDtl
读取掩码数量,切勿编造
子对象详情是按对象统计结果的唯一可信来源
completed
/
completed_with_errors
/
failed
为终端状态
running
/
scheduled
报告为最终状态是错误的
仅通过运行API的中止端点执行中止操作通过DML/删除任务记录并非真正的中止操作,会破坏状态
中止后务必重新查询状态并确认
canceled
中止调用返回200并不代表任务已停止
Data Mask仅在沙盒环境中运行在生产环境中调用运行/中止端点会返回
403
使用API版本
v67.0
或更高版本,且路径中不包含
/connect/
运行/中止端点路径为
/services/data/v67.0/platform/data-resilience/data-mask/...
——包含
connect
段或低于v67的版本会返回
NOT_FOUND
通过
scripts/poll-job.sh
进行轮询(单次调用),切勿手动编写SOQL循环
该脚本会限制尝试次数,并基于真实的详情行终止轮询;手动循环依赖延迟的父任务状态是导致任务超时且无报告的头号原因

Gotchas

常见问题

IssueResolution
sf sobject describe DataMaskPolicy
NOT_FOUND
It's a Tooling/MDAPI entity — query with
--use-tooling-api
, don't retry standard API
SELECT ... FROM DataMaskPolicy
INVALID_TYPE
Same cause — use Tooling API for the policy; standard API only for
DataMaskPolicyJobRun
/
Dtl
Run start returns
409
A run is already in progress for that policy — poll the existing one or wait for it to finish
Abort returns
409
"status=PENDING"
The job is still
pending
/
scheduled
, not yet
running
— keep polling and abort only once it reads
running
; don't give up on the abort
Small job finishes before you can abort itThe
running
window is seconds on a small sandbox — start a fresh run and poll tightly; never substitute a previously-aborted job to fake the flow
Abort returns
200
but SOQL status still
running
Cancellation is async — keep polling the SOQL status until
canceled
; don't report success early
Run API says
CANCELED
but SOQL says
running
Case + surface differ: the run API is UPPERCASE, SOQL picklist is lowercase. Trust the SOQL value for terminal state
Job "finished" instantlyRe-check:
scheduled
is not terminal. Poll until a terminal value actually appears
Run/abort endpoint
NOT_FOUND
The path must be
/services/data/v67.0/platform/data-resilience/data-mask/...
— no
/connect/
segment, and version
v67.0
+ (Core 262). See
references/api-surface.md

问题解决方案
sf sobject describe DataMaskPolicy
NOT_FOUND
这是Tooling/MDAPI实体——使用
--use-tooling-api
查询,不要在标准API中重试
SELECT ... FROM DataMaskPolicy
INVALID_TYPE
原因同上——对策略使用Tooling API;仅对
DataMaskPolicyJobRun
/
Dtl
使用标准API
启动任务返回
409
该策略已有任务在运行中——轮询现有任务或等待其完成
中止操作返回
409
"status=PENDING"
任务仍处于
pending
/
scheduled
状态,尚未变为
running
——继续轮询,仅当状态变为
running
时执行中止;不要放弃中止操作
小型任务在你执行中止前已完成在小型沙盒中
running
窗口仅持续数秒——启动新任务并密集轮询;切勿使用已中止的旧任务伪造流程
中止操作返回
200
但SOQL状态仍为
running
取消操作是异步的——继续轮询SOQL状态直至变为
canceled
;不要提前报告成功
运行API返回
CANCELED
但SOQL返回
running
大小写和接口存在差异:运行API返回大写,SOQL选择列表返回小写。终端状态以SOQL值为准
任务“立即完成”重新检查:
scheduled
并非终端状态。轮询直至实际出现终端值
运行/中止端点返回
NOT_FOUND
路径必须为
/services/data/v67.0/platform/data-resilience/data-mask/...
——不包含
/connect/
段,且版本为
v67.0
+(Core 262)。详见
references/api-surface.md

Output Format

输出格式

Report the sections for the workflow you ran — do not add sections for the other one. Keep it tight — show each command once, at the step it belongs to; do not append a second "full command log" that repeats calls already shown. Prefer a compact table over prose; a reader should reach the key result in the first screenful.
Workflow A (mask & report):
  1. Policy used (Id + label, and whether reused or created)
  2. Run — job Id, final terminal status, masked-record count (from the detail object). Collapse the poll loop to one line (e.g. "polled 5×,
    running
    completed
    "); do not print a row per poll.
  3. Per-object results — from
    DataMaskPolicyJobRunDtl
    . Report the object-level counts the rows actually carry; if there are no field-level error rows, say so as an inference ("no field-level errors reported"), not as a claimed per-field success. See A5 for the exact phrasing.
  4. Commands run — already shown inline above; here just list any not yet shown. Do not re-paste the full sequence a second time.
Workflow B (cancel a run):
  1. Job cancelled — the job Id, that it was
    running
    when aborted, that the abort was issued via the run-API abort endpoint, and the re-queried
    canceled
    status.
  2. Commands run — as above, no re-pasting.
Accuracy notes that keep factuality high:
  • The run/abort REST response returns a 15-character
    jobRunId
    (e.g.
    1aGXK0000000uob
    ); SOQL returns the 18-character form of the same record (e.g.
    1aGXK0000000uob2AA
    ). They are the same job — when both appear, note that rather than presenting them as two IDs.
  • Do not assert a masked count, a per-field outcome, or a terminal status you did not actually query. Every number in the report must trace to a query result shown in the command log.

仅报告你运行的工作流对应的章节——不要添加另一个工作流的章节。保持简洁——每个命令仅在对应步骤中显示一次;不要附加重复已显示调用的“完整命令日志”。优先使用紧凑表格而非 prose;读者应能在第一屏中看到关键结果。
工作流A(掩码并报告):
  1. 使用的策略(ID + 标签,以及是否复用或新建)
  2. 任务运行情况——任务ID、最终终端状态、已掩码记录数量(来自详情对象)。将轮询循环简化为一行(例如“轮询5次,
    running
    completed
    ”);不要打印每次轮询的记录。
  3. 按对象统计的结果——来自
    DataMaskPolicyJobRunDtl
    。报告记录中实际包含的对象级数量;如果没有字段级错误行,将其作为推断陈述(“未报告字段级错误”),不要声称每个字段都成功。详见A5中的准确表述。
  4. 运行的命令——已在上述步骤中内联显示;此处仅列出未显示的命令。不要再次粘贴完整命令序列。
工作流B(取消任务):
  1. 已取消的任务——任务ID、中止时状态为
    running
    、通过运行API中止端点执行中止操作,以及重新查询得到的
    canceled
    状态。
  2. 运行的命令——如上所述,不要再次粘贴。
提升事实准确性的注意事项:
  • 运行/中止REST响应返回15位
    jobRunId
    (例如
    1aGXK0000000uob
    );SOQL返回同一记录的18位格式(例如
    1aGXK0000000uob2AA
    )。它们是同一个任务——当两者同时出现时,请注明这一点,不要将其视为两个不同的ID。
  • 不要断言你未实际查询到的掩码数量、字段级结果或终端状态。报告中的每个数字都必须能追溯到命令日志中显示的查询结果。

Cross-Skill Integration

跨技能集成

NeedDelegate toReason
Seed realistic PII records to maskplatform-data-manageTest-data creation
Author custom anonymization Apexplatform-apex-generateApex authoring
Deploy the policy metadata to the orgplatform-metadata-deployMetadata deployment

需求委托至理由
植入真实的PII记录以便掩码platform-data-manage测试数据创建
编写自定义匿名化Apex代码platform-apex-generateApex代码编写
将策略元数据部署至组织platform-metadata-deploy元数据部署

Reference File Index

参考文件索引

FileWhen to read
references/api-surface.md
Exact per-entity API surface, all CLI commands, run/abort REST endpoints, and status picklist values
references/policy-authoring.md
Two-step authoring recipe (MDAPI thin shell → Tooling object/field inserts) and the
MaskingCategory
/
MaskValue
treatment table
references/run-and-abort.md
The run → poll → report → re-run → abort sequence in full, with sample responses
scripts/poll-job.sh
Bounded poller: waits for a terminal status (default) or, with
POLL_MODE=running
, for the abortable
running
window
文件阅读场景
references/api-surface.md
各实体的准确API接口、所有CLI命令、运行/中止REST端点,以及状态选择列表值
references/policy-authoring.md
两步法创建指南(MDAPI轻量外壳 → Tooling对象/字段插入)以及
MaskingCategory
/
MaskValue
处理规则表
references/run-and-abort.md
完整的运行 → 轮询 → 报告 → 重新运行 → 中止流程,包含示例响应
scripts/poll-job.sh
有限轮询工具:默认等待终端状态,设置
POLL_MODE=running
时等待可中止的
running
窗口