automation-sandbox-post-copy-configure

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Automation: Sandbox Post-Copy Configure

自动化:沙箱复制后配置

Apply a Salesforce sandbox post-copy automation JSON config to a target org. The skill pins the Tooling API sobject and the record-lookup SOQL filter for the two canonical
ConfigurationName
values it has been calibrated against (
OutboundMessages
,
RemoteSiteSettings
); for any other
ConfigurationName
it derives a candidate from the entry value and verifies it against the live org's describe endpoint. Every entry — pinned or derived — must still pass Step B (describe returns 200 with a
Metadata
compound field) before any PATCH is planned. Entries whose API cannot be identified or verified are surfaced in the summary and skipped — they are never guessed at.
将Salesforce沙箱复制后自动化JSON配置应用到目标组织。该技能针对已校准的两个标准
ConfigurationName
值(
OutboundMessages
RemoteSiteSettings
),固定对应的Tooling API sobject以及记录查找SOQL过滤器;对于其他任何
ConfigurationName
,它会从条目值推导候选对象,并针对实时组织的describe端点进行验证。无论条目是固定类型还是推导类型,在计划执行任何PATCH操作前,都必须通过步骤B(describe返回200状态且包含
Metadata
复合字段)。无法识别或验证API的条目会在摘要中显示并被跳过——绝不进行猜测。

Tool Restrictions

工具限制

Use ONLY the Bash tool to execute
sf
CLI commands (
sf data query --use-tooling-api
,
sf api request rest
,
sf org display
). Do NOT use MCP tools like
execute_soql
— ignore them completely; the compound
Metadata
PATCH pattern this skill requires is not available through MCP tool wrappers. If the target org alias is not explicitly named by the user, invoke
sf
commands without
--target-org
— the harness has already set the CLI's default target-org. Never pass
--target-org default
default
is not an alias and will fail with
NamedOrgNotFoundError
.
SOQL-over-REST (
sf data query --use-tooling-api ...
) is treated as an API call — same OAuth session, same authorization boundary as the subsequent PATCH. No direct database / non-Salesforce SQL access.
仅使用Bash工具执行
sf
CLI命令(
sf data query --use-tooling-api
sf api request rest
sf org display
)。请勿使用MCP工具如
execute_soql
——完全忽略它们;该技能所需的复合
Metadata
PATCH模式无法通过MCP工具包装器实现。如果用户未明确指定目标组织别名,执行
sf
命令时不要添加
--target-org
参数——运行环境已设置CLI的默认目标组织。绝不要传递
--target-org default
——
default
不是有效的别名,会触发
NamedOrgNotFoundError
错误。
SOQL-over-REST(
sf data query --use-tooling-api ...
)被视为API调用——使用相同的OAuth会话和授权边界,与后续的PATCH操作一致。不允许直接访问数据库或非Salesforce SQL。

STOP — do this before making any API call

停止——执行任何API调用前请完成以下步骤

Never call the org from memory. Before the first request:
  1. Read the config JSON end-to-end from the exact path the user gave (default
    ./post-copy-config.json
    ). Every entry must have all five keys (
    ConfigurationName
    ,
    Label
    ,
    Fields
    ,
    IsActive
    ,
    ExecutionOrder
    ). If any entry is malformed, abort and surface the file path + entry index — do not partially apply. Do not invent entries. If the file is missing, stop and ask; never fabricate a plan against synthetic labels.
  2. For each distinct
    ConfigurationName
    in the config, run the derivation + describe-verify step (below) before planning any PATCH. An entry whose API cannot be verified must never appear as a planned PATCH — it is rejected up front.
  3. Confirm the target org alias with the user unless they supplied one explicitly. This skill mutates a live org — writing to the wrong org (e.g., a production alias set as default) is the most expensive failure mode.
If you announce "I will apply … now" without having read the config file and run the describe-verify for every distinct
ConfigurationName
, stop and do those first.

绝不要凭记忆调用组织接口。首次请求前:
  1. 从用户指定的精确路径(默认值
    ./post-copy-config.json
    )完整读取配置JSON。每个条目必须包含全部五个键(
    ConfigurationName
    Label
    Fields
    IsActive
    ExecutionOrder
    )。如果任何条目格式错误,立即终止操作并显示文件路径+条目索引——不要部分应用请勿自行创建条目。如果文件缺失,停止操作并询问用户;绝不要基于虚构标签制定执行计划。
  2. 针对配置中的每个唯一
    ConfigurationName
    ,在计划任何PATCH操作前,先运行推导+验证步骤(如下)。无法验证API的条目绝不能出现在计划的PATCH操作中——需提前拒绝。
  3. 除非用户明确提供目标组织别名,否则请与用户确认该信息。该技能会修改实时组织——写入错误的组织(例如设置为默认的生产环境别名)是代价最高的故障场景。
如果您在未读取配置文件且未对每个唯一
ConfigurationName
执行describe验证的情况下宣布“我将立即应用……”,请停止操作并先完成这些步骤。

Per-entry procedure (the core of this skill)

每条目执行流程(技能核心)

The skill carries a pinned mapping for the canonical types below, and a derive-then-verify path for anything else. Follow these steps for every entry, in order.
Step B
(describe-verify) is mandatory regardless of whether the mapping came from the pinned table or the derive path — its HTTP status must appear in the summary.
该技能针对以下标准类型包含固定映射,对其他类型则采用“推导+验证”流程。请按顺序为每个条目执行以下步骤。无论映射来自固定表还是推导路径,步骤B(describe验证)都是强制性的——其HTTP状态必须显示在摘要中。

Step A — Resolve the Tooling API sobject and lookup filter

步骤A——解析Tooling API sobject和查找过滤器

Pinned canonicals (authoritative — use these exactly, do NOT substitute a different sobject name):
ConfigurationNameTooling API sobjectRecord-lookup SOQL
OutboundMessages
WorkflowOutboundMessage
SELECT Id, FullName FROM WorkflowOutboundMessage WHERE EntityDefinition.QualifiedApiName = '<Fields.Object>'
— then client-side pick the row whose
FullName == '<Fields.Object>.<Label>'
. SOQL cannot filter on
FullName
directly for this sobject.
RemoteSiteSettings
RemoteProxy
SELECT Id, SiteName FROM RemoteProxy WHERE SiteName = '<Label>'
.
For canonical entries use the pinned sobject and SOQL as-is; skip "derive". Do not invent
OutboundMessage
,
RemoteSiteSetting
, or
MasterLabel
variants — plausible-looking but wrong.
Field-name resolution (overrides + case rule + existence check) is owned by
scripts/map-metadata-key.mjs
.
Non-canonical: derive a candidate (usually singular of the Metadata API type name, sometimes prefixed); Step B rules out wrong guesses. Use the generic SOQL patterns in Step C.
标准固定映射(权威——请严格使用,不要替换为其他sobject名称):
ConfigurationNameTooling API sobject记录查找SOQL
OutboundMessages
WorkflowOutboundMessage
SELECT Id, FullName FROM WorkflowOutboundMessage WHERE EntityDefinition.QualifiedApiName = '<Fields.Object>'
— 然后在客户端选择
FullName == '<Fields.Object>.<Label>'
的行。该sobject无法通过SOQL直接过滤
FullName
RemoteSiteSettings
RemoteProxy
SELECT Id, SiteName FROM RemoteProxy WHERE SiteName = '<Label>'
对于标准条目,请直接使用固定的sobject和SOQL;跳过“推导”步骤。请勿自行创建
OutboundMessage
RemoteSiteSetting
MasterLabel
等变体——这些看似合理但实际错误。
字段名称解析(覆盖规则+大小写规则+存在性检查)由
scripts/map-metadata-key.mjs
负责。
非标准条目:推导候选对象(通常是Metadata API类型名称的单数形式,有时带前缀);步骤B会排除错误猜测。使用步骤C中的通用SOQL模式。

Step B — Verify the candidate exists and supports Metadata writes

步骤B——验证候选对象是否存在并支持Metadata写入

For each candidate, GET the describe:
bash
sf api request rest \
  "/services/data/v<apiVersion>/tooling/sobjects/<Candidate>/describe/"
Accept the candidate only if both are true:
  • HTTP 200 (the sobject exists on this org's Tooling API), AND
  • The describe response's
    fields
    array contains a field named
    Metadata
    (compound field — this is what PATCH writes through).
If no candidate passes both gates, mark the entry
API_NOT_IDENTIFIED
and skip. Do not guess a REST path — the wrong path 404s in the best case and updates the wrong record in the worst case.
针对每个候选对象,执行describe GET请求:
bash
sf api request rest \\
  "/services/data/v<apiVersion>/tooling/sobjects/<Candidate>/describe/"
仅当以下两个条件同时满足时,才接受候选对象:
  • HTTP状态码为200(该sobject在当前组织的Tooling API中存在),并且
  • describe响应的
    fields
    数组中包含名为
    Metadata
    的字段(复合字段——PATCH操作将通过该字段写入)。
如果没有候选对象通过这两个检查,标记该条目为
API_NOT_IDENTIFIED
并跳过。请勿猜测REST路径——错误路径在最佳情况下会返回404,最坏情况下会更新错误的记录。

Step C — Resolve the record Id (SOQL-over-REST)

步骤C——解析记录ID(SOQL-over-REST)

For pinned canonicals: run the SOQL from the Step A table verbatim (substituting
<Label>
/
<Fields.Object>
). Do not substitute a different filter column such as
MasterLabel
— the pinned SOQL is the tested-and-correct filter for that sobject.
For non-canonical (derived) sobjects: query the verified sobject for the record identified by the entry's
Label
(and
Fields.Object
when present):
bash
undefined
标准固定条目:严格执行步骤A表格中的SOQL(替换
<Label>
/
<Fields.Object>
)。不要替换为其他过滤列如
MasterLabel
——固定的SOQL是针对该sobject经过测试的正确过滤方式。
非标准(推导)sobject:查询已验证的sobject,找到与条目中
Label
(若存在则包含
Fields.Object
)匹配的记录:
bash
undefined

SOQL to a variable + separate jq call — avoids the most common

将SOQL存入变量 + 单独调用jq — 避免该技能中最常见的shell引号错误。

shell-quoting failure in this skill.

SOQL='SELECT Id, FullName FROM <VerifiedSobject> WHERE <UniqueFilter>' sf data query --use-tooling-api --json --query "$SOQL" \
/tmp/entry-<Slug>-lookup.json ID=$(jq -r '.result.records[0].Id // empty' /tmp/entry-<Slug>-lookup.json)

Pick `<UniqueFilter>` based on the queryable fields shown in the
describe response from Step B. If the row contains a `FullName` field,
SOQL usually rejects a direct `FullName = ...` filter — filter by
whichever direct column the describe surfaces (e.g.
`EntityDefinition.QualifiedApiName`, `DeveloperName`, `SiteName`) and
apply the `FullName` match client-side (`jq -r
'.result.records[] | select(.FullName == "<Object>.<Label>") | .Id'`).

Outcomes:

- Zero rows → `NOT_FOUND` (never fall back to insert).
- Multiple rows after client-side filtering → `AMBIGUOUS` (surface
  all Ids in the summary and skip; a wrong Id is worse than no Id).
- Exactly one row → proceed to Step D.
SOQL='SELECT Id, FullName FROM <VerifiedSobject> WHERE <UniqueFilter>' sf data query --use-tooling-api --json --query "$SOQL" \
/tmp/entry-<Slug>-lookup.json ID=$(jq -r '.result.records[0].Id // empty' /tmp/entry-<Slug>-lookup.json)

根据步骤B中describe响应显示的可查询字段选择`<UniqueFilter>`。如果行包含`FullName`字段,SOQL通常会拒绝直接使用`FullName = ...`过滤——请使用describe返回的直接列进行过滤(例如`EntityDefinition.QualifiedApiName`、`DeveloperName`、`SiteName`),然后在客户端进行`FullName`匹配(`jq -r '.result.records[] | select(.FullName == "<Object>.<Label>") | .Id'`)。

结果:

- 零行 → `NOT_FOUND`(绝不回退到插入操作)。
- 客户端过滤后有多行 → `AMBIGUOUS`(在摘要中显示所有ID并跳过;错误ID比无ID更糟糕)。
- 恰好一行 → 进入步骤D。

Step D — GET current Metadata, mutate, PATCH back

步骤D——获取当前Metadata、修改并PATCH回传

Metadata
is replace-in-full — omitted keys are blanked on PATCH. Preserve every existing key from the Step D-1 GET; overlay only the mutated keys. The summary's "planned PATCH body" (apply and dry-run) is this full merged object — verbose or org-specific values may render as
"<preserved-from-GET>"
in dry-run, but every key must be present.
Every Step D filename must include the record
<Id>
from Step C (e.g.
/tmp/entry-<Id>-meta.json
) — phase-parallel entries share the working directory and would clobber a shared name.
  1. GET current
    Metadata
    /tmp/entry-<Id>-meta.json
    .
  2. Resolve the mutation key — for each
    Fields.<Xxx>
    , run
    node scripts/map-metadata-key.mjs "<ConfigurationName>" "<ConfigFieldName>" /tmp/entry-<Id>-meta.json
    . On
    {"status":"OK","key":...}
    use the returned key. On
    {"status":"FIELD_MAP_UNKNOWN",...}
    mark the entry and skip.
  3. PATCH — heredoc-build
    /tmp/entry-<Id>-mutation.json
    , merge with
    jq --slurpfile m /tmp/entry-<Id>-mutation.json '. + $m[0] | {Metadata: .}' /tmp/entry-<Id>-meta.json > /tmp/entry-<Id>-patch.json
    (preserves JSON types), PATCH with
    -b @/tmp/entry-<Id>-patch.json
    , capture the response body to
    /tmp/entry-<Id>-response.json
    . Full bash in
    references/api_endpoints.md
    §Step D. Never use
    --arg
    (stringifies booleans/numbers, breaks on special chars). Wrap the PATCH in
    for attempt in 1 2; do <cmd> && break; done
    — retry once on shell/jq quoting failure (non-zero exit before the HTTP call goes out).
  4. Classify
    node scripts/classify-patch-result.mjs "<httpCode>" /tmp/entry-<Id>-response.json
    . Exit 0 →
    SUCCESS
    ; exit 2 →
    FAILED
    (parsed error on stdout). 204 with a non-empty body is
    FAILED
    .
Metadata
采用完全替换模式——PATCH中省略的键会被清空。保留步骤D-1 GET请求返回的所有现有键;仅覆盖需要修改的键。摘要中的“计划PATCH体”(应用和试运行)是完整的合并对象——试运行中冗长或组织特定的值可能显示为
"<preserved-from-GET>"
,但必须包含所有键。
每个步骤D的文件名必须包含步骤C中获取的记录
<Id>
(例如
/tmp/entry-<Id>-meta.json
)——并行阶段的条目共享工作目录,共享名称会导致文件覆盖。
  1. GET当前
    Metadata
    /tmp/entry-<Id>-meta.json
  2. 解析修改键 — 针对每个
    Fields.<Xxx>
    ,运行
    node scripts/map-metadata-key.mjs "<ConfigurationName>" "<ConfigFieldName>" /tmp/entry-<Id>-meta.json
    。如果返回
    {"status":"OK","key":...}
    ,使用返回的键。如果返回
    {"status":"FIELD_MAP_UNKNOWN",...}
    ,标记该条目并跳过。
  3. PATCH — 通过heredoc构建
    /tmp/entry-<Id>-mutation.json
    ,使用
    jq --slurpfile m /tmp/entry-<Id>-mutation.json '. + $m[0] | {Metadata: .}' /tmp/entry-<Id>-meta.json > /tmp/entry-<Id>-patch.json
    进行合并(保留JSON类型),使用
    -b @/tmp/entry-<Id>-patch.json
    执行PATCH,将响应体保存到
    /tmp/entry-<Id>-response.json
    。完整的bash命令见
    references/api_endpoints.md
    的步骤D。绝不要使用
    --arg
    (会将布尔值/数字转为字符串,特殊字符会导致错误)。将PATCH命令包裹在
    for attempt in 1 2; do <cmd> && break; done
    中——如果shell/jq引号错误(HTTP请求发送前返回非零退出码),重试一次。
  4. 分类结果 — 运行
    node scripts/classify-patch-result.mjs "<httpCode>" /tmp/entry-<Id>-response.json
    。退出码0 →
    SUCCESS
    ;退出码2 →
    FAILED
    (标准输出会解析错误信息)。状态码204但响应体非空 →
    FAILED

Step E — Verify the change landed

步骤E——验证修改是否生效

Re-read the record to confirm the PATCH stuck:
  • If the mutated field is a direct-queryable SOQL column on the sobject (rare — most
    Metadata
    -writable fields are not), a SELECT by Id is enough.
  • Otherwise GET the record and inspect the corresponding key inside
    .Metadata
    .
If the read-back value doesn't match the requested value, record
FAILED_VERIFY
— the PATCH returned 204 but the effect is not visible (usually a naming or permission issue).

重新读取记录以确认PATCH操作成功:
  • 如果修改的字段是该sobject上可直接查询的SOQL列(罕见——大多数可通过
    Metadata
    写入的字段无法直接查询),通过ID执行SELECT即可。
  • 否则,GET该记录并检查
    .Metadata
    中对应的键。
如果读取的值与请求的值不匹配,记录为
FAILED_VERIFY
——PATCH返回204但效果不可见(通常是命名或权限问题)。

IsActive semantics

IsActive语义

  • IsActive: true
    → apply the PATCH as described in Steps A–E.
  • IsActive: false
    do not PATCH. Record
    SKIPPED_INACTIVE
    for the entry and add a bullet under Follow-ups in the summary file listing the entry's
    ConfigurationName
    +
    Label
    so the customer notices that a config-declared inactive record was left untouched on the target org.
Rationale:
IsActive: false
means "not active in this sandbox", not "deactivate the existing record". Silently deactivating a live integration is a much bigger blast radius than leaving it alone.

  • IsActive: true
    → 按步骤A–E执行PATCH操作。
  • IsActive: false
    不执行PATCH。标记该条目为
    SKIPPED_INACTIVE
    ,并在摘要文件的后续操作部分添加项目符号,列出条目的
    ConfigurationName
    +
    Label
    ,以便客户注意到配置中声明为非活动的记录在目标组织中未被修改。
原理:
IsActive: false
表示“在此沙箱中不激活”,而非“停用现有记录”。静默停用实时集成的影响远大于保持原样。

Canonical output shape (always emit this)

标准输出格式(必须严格遵循)

Single Markdown summary written to
./post-copy-<mode>-summary.md
(mode is
dry-run
or
apply
) AND printed to the user. No JSON side-files (
plan/phases.json
,
requests/*.request.json
, etc.) — inline every planned/actual request in the Markdown.
Phase enumeration is script-owned. Run
node scripts/plan-phases.mjs <config.json>
and consume its
phases[]
output verbatim. Each entry carries
ordinal
(1-indexed phase number) and
executionOrder
(raw value, for the
(ExecutionOrder = <raw>)
heading annotation). Sparse values collapse (
1, 2, 5
→ ordinals
1, 2, 3
).
IsActive:false
entries are pre-marked
SKIP_INACTIVE
. See
references/execution_phasing.md
for the worked example.
For dry-run entries the
HTTP
column is
(em-dash). End the summary with:
No PATCH requests were issued. To apply, re-run without the dry-run flag.
Target-org resolution is script-owned. Run
node scripts/resolve-target-org.mjs
; substitute the returned
.alias
into the header. Never emit
<env:SF_TARGET_ORG>
or
$SF_TARGET_ORG
verbatim.
markdown
undefined
生成单个Markdown摘要文件保存到
./post-copy-<mode>-summary.md
(mode为
dry-run
apply
),同时打印给用户。不要生成JSON辅助文件(
plan/phases.json
requests/*.request.json
等)——将所有计划/实际请求内联到Markdown中。
阶段枚举由脚本负责。运行
node scripts/plan-phases.mjs <config.json>
并直接使用其
phases[]
输出。每个条目包含
ordinal
(从1开始的阶段编号)和
executionOrder
(原始值,用于标题注释
(ExecutionOrder = <raw>)
)。稀疏值会自动合并(例如
1, 2, 5
→ 序号
1, 2, 3
)。
IsActive:false
的条目会被预先标记为
SKIP_INACTIVE
。示例见
references/execution_phasing.md
对于试运行条目,
HTTP
列显示
(破折号)。摘要结尾需添加:
未发出任何PATCH请求。如需应用,请重新运行并移除试运行标志。
目标组织解析由脚本负责。运行
node scripts/resolve-target-org.mjs
;将返回的
.alias
替换到标题中。绝不要直接输出
<env:SF_TARGET_ORG>
$SF_TARGET_ORG
markdown
undefined

Post-Copy Configure Run — <N> entries <planned|applied> against
<alias>

复制后配置执行 — 针对
<alias>
组织<计划|应用>了<N>个条目

Config file:
<path>
Target org:
<alias>
Mode: <dry-run|apply>
配置文件:
<path>
目标组织:
<alias>
模式:<dry-run|apply>

Phase <ExecutionOrder><count> entr(y|ies)

阶段<ExecutionOrder><count>个条目

Planned request:
  • Method:
    PATCH
  • Path:
    /services/data/v<apiVersion>/tooling/sobjects/<VerifiedSobject>/<Id>
  • Body:
    <JSON — the FULL merged Metadata object: every existing key from the Step D-1 GET, overlaid with the mutated keys from the config. Preserved keys with verbose or org-specific values may appear as "<preserved-from-GET>" placeholder strings, but every key must be present. NEVER emit a minimal body containing only the mutated keys.>
ConfigurationNameLabelObjectSobjectDescribeOutcomeHTTP
<name><lbl><obj><VerifiedSobject><200 or 404><outcome><code>
计划请求:
  • 方法:
    PATCH
  • 路径:
    /services/data/v<apiVersion>/tooling/sobjects/<VerifiedSobject>/<Id>
  • 请求体:
    <JSON — 完整的合并Metadata对象:包含步骤D-1 GET请求返回的所有现有键,叠加配置中的修改键。冗长或组织特定的保留键可能显示为"<preserved-from-GET>"占位符字符串,但必须包含所有键。绝不允许仅包含修改键的极简请求体。>
ConfigurationNameLabel对象SobjectDescribe结果HTTP
<name><lbl><obj><VerifiedSobject><200或404><outcome><code>

Totals

总计

OutcomeCount
<state><n>
结果数量
<state><n>

Follow-ups

后续操作

  • <bullet per SKIPPED_INACTIVE / API_NOT_IDENTIFIED / AMBIGUOUS / FIELD_MAP_UNKNOWN / NOT_FOUND entry>

Column semantics: `Object` = the entry's `Fields.Object` if present,
`—` otherwise. `Sobject` = the Step A resolved sobject. `Describe` =
the Step B describe HTTP status (`200` for verified, `404` for
`API_NOT_IDENTIFIED`) — mandatory so skipped Step Bs are visible at a
glance. Outcome vocabulary: `SUCCESS`, `NOT_FOUND`, `AMBIGUOUS`,
`API_NOT_IDENTIFIED`, `FIELD_MAP_UNKNOWN`, `FAILED`, `FAILED_VERIFY`,
`SKIPPED_INACTIVE`, `SKIPPED`, `DRY_RUN`, `DELETE_NOT_SUPPORTED`,
`NOT_ATTEMPTED`.

**Scripts are internal.** The `plan-phases.mjs`, `map-metadata-key.mjs`,
`classify-patch-result.mjs`, `resolve-target-org.mjs` invocations are
implementation detail — do **not** inline their raw stdout, JSON
output, or "I ran node …" narration into the summary Markdown or
the printed response. Consume the JSON, use the returned values, and
render the summary in the exact shape above.
  • <每个SKIPPED_INACTIVE / API_NOT_IDENTIFIED / AMBIGUOUS / FIELD_MAP_UNKNOWN / NOT_FOUND条目对应一个项目符号>

列语义:`对象` = 条目中的`Fields.Object`(若存在),否则为`—`。`Sobject` = 步骤A解析的sobject。`Describe` = 步骤B的describe HTTP状态码(验证通过为`200`,`API_NOT_IDENTIFIED`为`404`)——必须显示,以便一眼看出哪些条目跳过了步骤B。结果词汇:`SUCCESS`、`NOT_FOUND`、`AMBIGUOUS`、`API_NOT_IDENTIFIED`、`FIELD_MAP_UNKNOWN`、`FAILED`、`FAILED_VERIFY`、`SKIPPED_INACTIVE`、`SKIPPED`、`DRY_RUN`、`DELETE_NOT_SUPPORTED`、`NOT_ATTEMPTED`。

**脚本为内部实现**。`plan-phases.mjs`、`map-metadata-key.mjs`、`classify-patch-result.mjs`、`resolve-target-org.mjs`的调用属于实现细节——**不要**将其原始标准输出、JSON输出或“我运行了node……”的描述内联到摘要Markdown或打印给用户的响应中。请解析JSON,使用返回的值,并严格按照上述格式生成摘要。

Scope

范围

  • In scope: Reading a post-copy config JSON (the shape produced by
    automation-sandbox-post-copy-config-generate
    ), grouping entries into
    ExecutionOrder
    phases, deriving+verifying the Tooling API sobject for each entry, resolving Ids via SOQL-over-REST, PATCHing via compound
    Metadata
    , and reporting per-entry outcomes.
  • Out of scope: Generating the config JSON from an SOP (delegate to
    automation-sandbox-post-copy-config-generate
    ); deploying metadata XML; running the Async Task Framework (ATF) orchestrator itself (that is the platform-side Java implementation); inventing API paths for
    ConfigurationName
    values whose describe probe fails (surface as
    API_NOT_IDENTIFIED
    and stop).
Every API call is against a live org. Treat this skill as a mutation tool: prefer
--dry-run
first, confirm the target org alias, and never silently retry a failed entry against a different endpoint.

  • 包含范围:读取复制后配置JSON(由
    automation-sandbox-post-copy-config-generate
    生成的格式),将条目按
    ExecutionOrder
    分组为阶段,推导+验证每个条目的Tooling API sobject,通过SOQL-over-REST解析ID,通过复合
    Metadata
    执行PATCH,以及报告每条目的结果。
  • 排除范围:从标准操作流程(SOP)生成配置JSON(委托给
    automation-sandbox-post-copy-config-generate
    );部署元数据XML;运行异步任务框架(ATF)编排器本身(这是平台端的Java实现);为describe探测失败的
    ConfigurationName
    值自行创建API路径(标记为
    API_NOT_IDENTIFIED
    并停止)。
所有API调用都针对实时组织。请将该技能视为修改工具:优先使用
--dry-run
,确认目标组织别名,绝不要针对不同端点静默重试失败的条目。

Required Inputs

必填输入

Gather or infer before applying:
  • Config file path: Path to the JSON produced by
    automation-sandbox-post-copy-config-generate
    (default:
    ./post-copy-config.json
    in the current directory). If the file does not exist, stop and ask.
  • Target org alias / username: The
    sf
    CLI alias or username of the target sandbox. Never assume the default org — always confirm. If the user has not supplied one, list available orgs with
    sf org list --json
    and ask which to use.
  • Dry-run flag (optional, default
    false
    ): Semantics — reads are ALLOWED, writes are FORBIDDEN. Dry-run means the run produces no mutation on the target org; it does NOT mean "no network calls". You MUST still execute every read against the org: the describe probe (Step B,
    GET
    ), the SOQL Id lookup (Step C,
    sf data query
    ), and the record fetch (Step D-1,
    GET
    ) — because the plan's "would-be PATCH body" can only be accurate if the agent has read the current
    Metadata
    block from the live record. The ONLY skipped calls are the writes: Step D-3 (
    PATCH
    ) and Step E (post-PATCH verification). Never fabricate the current Metadata; never emit a body containing invented keys. If a read fails (401, 404,
    NamedOrgNotFoundError
    ), surface the error and stop — do not fall back to a from-memory plan. Use dry-run on the first pass unless the user has explicitly asked to apply.
  • Continue-on-error (optional, default
    true
    ): When true, a failing entry does not abort the phase — the remaining entries in the phase still execute, and the failure is reported in the summary. When false, a failure aborts execution mid-phase.
If the user supplies a clear config path and target alias, proceed without further questions.

应用前需收集或推断:
  • 配置文件路径:由
    automation-sandbox-post-copy-config-generate
    生成的JSON文件路径(默认:当前目录下的
    ./post-copy-config.json
    )。如果文件不存在,停止操作并询问用户。
  • 目标组织别名/用户名:目标沙箱的
    sf
    CLI别名或用户名。绝不要假设默认组织——始终确认。如果用户未提供,使用
    sf org list --json
    列出可用组织并询问用户选择哪个。
  • 试运行标志(可选,默认
    false
    ):语义——允许读取,禁止写入。试运行意味着操作不会修改目标组织;并不意味着“无网络调用”。您仍必须针对组织执行所有读取操作:describe探测(步骤B,
    GET
    )、SOQL ID查找(步骤C,
    sf data query
    )、记录获取(步骤D-1,
    GET
    )——因为只有从实时记录读取当前
    Metadata
    块,计划的“拟执行PATCH体”才能准确。唯一跳过的调用是写入操作:步骤D-3(
    PATCH
    )和步骤E(PATCH后验证)。绝不要虚构当前Metadata;绝不要输出包含虚构键的请求体。如果读取失败(401、404、
    NamedOrgNotFoundError
    ),显示错误并停止——不要回退到基于记忆的计划。除非用户明确要求应用,否则首次运行请使用试运行模式。
  • 错误继续(可选,默认
    true
    ):设为true时,失败的条目不会终止阶段——阶段中的剩余条目仍会执行,失败情况会在摘要中报告。设为false时,失败会立即终止阶段执行。
如果用户提供了明确的配置路径和目标别名,可直接继续操作,无需进一步询问。

Workflow

工作流程

Every step executes real
sf
CLI commands via the Bash tool. Do NOT narrate the plan without actually running the commands — the point of this skill is to mutate the target org, not to describe how.
  1. Read and validate the config JSON — load the file with the Read tool. Every entry must be a JSON object with the five top-level keys (
    ConfigurationName
    ,
    Label
    ,
    Fields
    ,
    IsActive
    ,
    ExecutionOrder
    ). Malformed entries are a hard stop.
  2. Resolve the target org and API version — run
    sf org display --json
    . Parse the JSON to confirm the alias resolves and to capture
    result.apiVersion
    (e.g.
    62.0
    ). Do not print the raw JSON — it contains the access token.
  3. Plan phases — run
    node scripts/plan-phases.mjs <config.json>
    and iterate its
    phases[]
    output. See
    references/execution_phasing.md
    for the concurrency cap.
  4. Per-entry describe-verify pass — for each distinct
    ConfigurationName
    , run Step A + Step B once and cache the verified sobject. Any
    ConfigurationName
    failing Step B marks every entry with that name as
    API_NOT_IDENTIFIED
    .
  5. Per-entry apply pass — for each entry inside each phase:
    • Entries pre-marked
      SKIP_INACTIVE
      by plan-phases → record
      SKIPPED_INACTIVE
      , add a Follow-ups bullet, continue.
    • Else run Step C (resolve Id), then Step D (GET+mutate+PATCH), then Step E (verify). If
      --dry-run
      is true, still run Step C and D-1/D-2 so the printed plan reflects the real merged payload — skip only D-3 (the PATCH) and Step E, then record
      DRY_RUN
      .
  6. Between phases — wait for every entry in the current phase to complete before starting the next.
  7. Write the summary file to disk — the final deliverable is a Markdown file at
    ./post-copy-<mode>-summary.md
    , following the shape in the "Canonical output shape" section. Also print it to the user. Never write access tokens or full
    sf org display
    output. If a URL contains embedded credentials, mask them in the summary (
    https://user:***@host/...
    ); the actual PATCH body carries the verbatim URL.

每个步骤都通过Bash工具执行真实的
sf
CLI命令。不要仅描述计划而不实际运行命令——该技能的目的是修改目标组织,而非描述操作方式。
  1. 读取并验证配置JSON — 使用读取工具加载文件。每个条目必须是包含五个顶级键(
    ConfigurationName
    Label
    Fields
    IsActive
    ExecutionOrder
    )的JSON对象。格式错误的条目会导致操作立即终止。
  2. 解析目标组织和API版本 — 运行
    sf org display --json
    。解析JSON以确认别名有效,并捕获
    result.apiVersion
    (例如
    62.0
    )。不要打印原始JSON——其中包含访问令牌。
  3. 计划阶段 — 运行
    node scripts/plan-phases.mjs <config.json>
    并遍历其
    phases[]
    输出。并发限制见
    references/execution_phasing.md
  4. 每条目describe验证遍历 — 针对每个唯一的
    ConfigurationName
    ,执行一次步骤A + 步骤B,并缓存已验证的sobject。任何未通过步骤B的
    ConfigurationName
    会将所有对应条目标记为
    API_NOT_IDENTIFIED
  5. 每条目应用遍历 — 针对每个阶段中的每个条目:
    • 被plan-phases预先标记为
      SKIP_INACTIVE
      的条目 → 记录为
      SKIPPED_INACTIVE
      ,添加后续操作项目符号,继续执行下一个条目。
    • 其他条目执行步骤C(解析ID),然后执行步骤D(GET+修改+PATCH),再执行步骤E(验证)。如果
      --dry-run
      为true,仍需执行步骤C和D-1/D-2,以便打印的计划反映真实的合并负载——仅跳过D-3(PATCH)和步骤E,然后记录为
      DRY_RUN
  6. 阶段间等待 — 等待当前阶段的所有条目完成后,再开始下一个阶段。
  7. 将摘要文件写入磁盘 — 最终交付物是保存到
    ./post-copy-<mode>-summary.md
    的Markdown文件,严格遵循“标准输出格式”部分的要求。同时打印给用户。绝不要写入访问令牌或完整的
    sf org display
    输出。如果URL包含嵌入的凭据,请在摘要中屏蔽(
    https://user:***@host/...
    );实际PATCH请求体保留原始URL。

Rules and Gotchas

规则与注意事项

Load-bearing invariants (never insert; describe-gate every
ConfigurationName
; compound
Metadata
PATCH preserves the full existing block;
IsActive: false
SKIPPED_INACTIVE
; never print raw access tokens; cap intra-phase concurrency at 5) and the canonical response for each runtime failure (401 mid-run, 429, credential-bearing URLs,
Fields.Action = "Delete"
, describe 404, zero/many SOQL rows) live in
references/rules_gotchas.md
. Read that file before deviating from the Step A–E procedure.

核心不变量(绝不插入;对每个
ConfigurationName
进行describe验证;复合
Metadata
PATCH保留完整现有块;
IsActive: false
SKIPPED_INACTIVE
;绝不打印原始访问令牌;阶段内并发限制为5)以及每个运行时故障的标准响应(运行中401、429、包含凭据的URL、
Fields.Action = "Delete"
、describe 404、SOQL零行/多行)均在
references/rules_gotchas.md
中。在偏离步骤A–E流程前,请先阅读该文件。

Cross-Skill Integration

跨技能集成

NeedDelegate to
Turn a customer SOP into the JSON config this skill consumes
automation-sandbox-post-copy-config-generate
Deploy Salesforce metadata XML (Custom Labels, Named Credentials, etc.) that lives outside the compound-Metadata Tooling API patternThe matching
generating-*
skill + a metadata deploy flow
Create new records that do not yet exist on the target org
platform-metadata-deploy
after generating the metadata XML
Assign permission sets required to run the API calls
dx-org-permission-set-assign

需求委托给
将客户的标准操作流程(SOP)转换为该技能可使用的JSON配置
automation-sandbox-post-copy-config-generate
部署复合Metadata Tooling API模式之外的Salesforce元数据XML(自定义标签、命名凭据等)匹配的
generating-*
技能 + 元数据部署流程
在目标组织中创建不存在的新记录生成元数据XML后使用
platform-metadata-deploy
分配运行API调用所需的权限集
dx-org-permission-set-assign

Reference File Index

参考文件索引

FileWhen to read
references/api_endpoints.md
Steps A–E — the full generic recipe with worked examples (OBM, RSS) and the camelCase-field convention
references/execution_phasing.md
Step 3 (workflow) — the grouping rules and the intra-phase concurrency cap.
scripts/plan-phases.mjs
is the executable source-of-truth for phase enumeration; this file explains the model behind it
scripts/plan-phases.mjs
Step 3 (workflow) — deterministic phase planner (invoke, then read its output)
scripts/map-metadata-key.mjs
Step D-2 — deterministic Metadata-key resolver (override table + case rule + existence check)
scripts/classify-patch-result.mjs
Step D-4 — deterministic HTTP-outcome classifier (SUCCESS vs FAILED)
scripts/resolve-target-org.mjs
Canonical output shape — deterministic target-org alias resolver
references/authentication.md
Step 2 (workflow) — for the session-check recipe and how to handle 401 mid-run
references/rules_gotchas.md
Before deviating from Step A–E — load-bearing invariants and canonical runtime-failure responses
assets/api_request_templates.json
Steps A–E — the generic template for describe / lookup / GET+PATCH with placeholder keys
examples/sample_config_input.json
Step 1 (workflow) — shape of the config JSON this skill consumes
examples/sample_execution_summary.md
Step 7 (workflow) — the shape of the summary report shown to the user
文件读取时机
references/api_endpoints.md
步骤A–E — 包含完整通用流程和示例(OBM、RSS)以及驼峰式字段约定
references/execution_phasing.md
步骤3(工作流程) — 分组规则和阶段内并发限制。
scripts/plan-phases.mjs
是阶段枚举的可执行权威来源;该文件解释其背后的模型
scripts/plan-phases.mjs
步骤3(工作流程) — 确定性阶段规划器(调用后读取其输出)
scripts/map-metadata-key.mjs
步骤D-2 — 确定性Metadata键解析器(覆盖表+大小写规则+存在性检查)
scripts/classify-patch-result.mjs
步骤D-4 — 确定性HTTP结果分类器(SUCCESS vs FAILED)
scripts/resolve-target-org.mjs
标准输出格式 — 确定性目标组织别名解析器
references/authentication.md
步骤2(工作流程) — 会话检查流程以及运行中401错误的处理方式
references/rules_gotchas.md
偏离步骤A–E流程前 — 核心不变量和标准运行时故障响应
assets/api_request_templates.json
步骤A–E — describe / 查找 / GET+PATCH的通用模板,包含占位符键
examples/sample_config_input.json
步骤1(工作流程) — 该技能可使用的配置JSON格式示例
examples/sample_execution_summary.md
步骤7(工作流程) — 展示给用户的摘要报告格式示例