openxiangda 1.0.207 → 1.0.208

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/README.md CHANGED
@@ -202,7 +202,7 @@ openxiangda resource publish function --code customer_get --change <change> --pr
202
202
 
203
203
  Exact `--only` / `--code` selectors are applied before unrelated manifests, source dependencies, and JS_CODE targets are read or built. Shared/transitive dependencies of the selected targets remain in scope; omitting a selector intentionally preserves full-workspace validation and planning. Resource commands use the packaged canonical scoped builder for standard workspaces, so an older checked-in `scripts/build-js-code.mjs` does not need to be upgraded before the installed CLI gains this optimization; refresh the workspace template only when developers also need the same behavior from a manual `pnpm build-js-code` command.
204
204
 
205
- For source-only Function/Automation changes, the final command does not reconstruct whole definitions with client-side GET+PUT. `release begin` captures the Git/change baseline, one preflight covers every selected code, and Backend Release performs one `prepare -> verify -> activate` sequence for all eligible updates. Inspect history with `openxiangda release backend-head|backend-list|backend-detail`, compare it with `backend-diff`, or create an audited immutable rollback with `backend-rollback <releaseId> --change <change> --reason "..."`. Always merge/push the frozen SHA and run `release end` when promotion finishes. A lost or expired lease retains the pending-mainline evidence instead of silently clearing it.
205
+ For source-only Function/Automation changes, the final command does not reconstruct whole definitions with client-side GET+PUT. `release begin` captures the Git/change baseline, one preflight covers every selected code, and Backend Release performs one `prepare -> verify -> activate` sequence for all eligible updates. Repository identity is alias-aware but fail-closed: when a clone-derived primary ID differs from the frozen source base, the CLI uses the frozen canonical ID only if it is present in `releaseSourceRevision.repoAliases`; Backend, Workflow, and Root App Release writes all carry that same ID, while a disjoint identity set fails before prepare. Inspect history with `openxiangda release backend-head|backend-list|backend-detail`, compare it with `backend-diff`, or create an audited immutable rollback with `backend-rollback <releaseId> --change <change> --reason "..."`. Always merge/push the frozen SHA and run `release end` when promotion finishes. A lost or expired lease retains the pending-mainline evidence instead of silently clearing it.
206
206
 
207
207
  Page repair publishing is staged by default. It first freezes `pages/snapshot`, sends the active Page Release parent plus every page revision, and uses revision `0` only for a genuinely new page. Review with `openxiangda page head|releases|detail|diff`; activate an immutable complete release explicitly with `page activate <releaseId> --change <change>`. Historical activation requires `page rollback <releaseId> --rollback --change <change> --reason "..."`. Parent or revision conflicts are never refreshed or retried automatically.
208
208
 
@@ -247,6 +247,53 @@ function normalizeManagedReleaseSourceRevision(
247
247
  };
248
248
  }
249
249
 
250
+ function normalizeReleaseSourceRevisionForBaseline(
251
+ target,
252
+ sourceBase,
253
+ sourceRevision,
254
+ options = {}
255
+ ) {
256
+ const revision = normalizeManagedReleaseSourceRevision(
257
+ target,
258
+ sourceRevision,
259
+ options
260
+ );
261
+ const expectedRepositoryId = String(
262
+ sourceBase?.repo || sourceBase?.repositoryId || ''
263
+ )
264
+ .trim()
265
+ .toLowerCase();
266
+ if (!expectedRepositoryId) return revision;
267
+ const repositoryAliases = normalizeIdentityList([
268
+ revision?.repo,
269
+ revision?.repositoryId,
270
+ ...(Array.isArray(revision?.repoAliases) ? revision.repoAliases : []),
271
+ ]);
272
+ if (!repositoryAliases.includes(expectedRepositoryId)) {
273
+ const code =
274
+ String(options.errorCode || '').trim() ||
275
+ 'RELEASE_SOURCE_REPOSITORY_MISMATCH';
276
+ const error = new Error(
277
+ `${code}: 当前 Git 仓库与冻结 change baseline 仓库不一致`
278
+ );
279
+ error.code = code;
280
+ error.details = {
281
+ expectedRepositoryId,
282
+ repositoryAliases,
283
+ };
284
+ throw error;
285
+ }
286
+ return {
287
+ ...revision,
288
+ repo: expectedRepositoryId,
289
+ repositoryId: expectedRepositoryId,
290
+ repoAliases: normalizeIdentityList([
291
+ expectedRepositoryId,
292
+ ...repositoryAliases,
293
+ ]),
294
+ };
295
+ }
296
+
250
297
  function normalizeManagedChangeSourceBase(
251
298
  target,
252
299
  sourceBase,
@@ -597,6 +644,7 @@ module.exports = {
597
644
  normalizeEnvironmentKind,
598
645
  normalizeManagedChangeSourceBase,
599
646
  normalizeManagedReleaseSourceRevision,
647
+ normalizeReleaseSourceRevisionForBaseline,
600
648
  normalizeSideEffectPolicyInput,
601
649
  normalizeTargetName,
602
650
  hasStateResourceMappings,
package/lib/cli.js CHANGED
@@ -136,6 +136,7 @@ const {
136
136
  normalizeEnvironmentKind,
137
137
  normalizeManagedChangeSourceBase,
138
138
  normalizeManagedReleaseSourceRevision,
139
+ normalizeReleaseSourceRevisionForBaseline,
139
140
  readCandidate,
140
141
  readJsonInput,
141
142
  releaseExecutionPath,
@@ -18210,8 +18211,9 @@ async function ensureOwnedImmutableReleasePublishContext(
18210
18211
  `${releaseKind} 只接受与本地冻结基线匹配的 stored lease;外部 opaque lease 不能用于原子发布`
18211
18212
  );
18212
18213
  }
18213
- const sourceRevision = normalizeManagedReleaseSourceRevision(
18214
+ const sourceRevision = normalizeReleaseSourceRevisionForBaseline(
18214
18215
  target,
18216
+ baseline.sourceBase || {},
18215
18217
  baseline.releaseSourceRevision || {},
18216
18218
  { errorCode: 'RELEASE_SOURCE_REPOSITORY_MISMATCH' }
18217
18219
  );
@@ -118,7 +118,7 @@ Reviewed bundle commands may retain `<profile>` as a template. The explicit real
118
118
 
119
119
  Exact `resourceSelectors` are authoritative for supported configuration resources such as `publicAccessPolicies`. A legacy `resources=true` category marker is narrowed by those exact selectors and must not become an app-wide generic-resource release. Missing selectors, unknown resource types, wildcard `*`, deletes, and genuine app-wide resource closures remain fail closed.
120
120
 
121
- `release begin --change` freezes a clean committed `HEAD` only when the current branch is the authoritative default `main`/`master` and its commit exactly equals the live remote tip. Before any live write, the CLI preflights the complete target set and rejects source changes during the release. A feature worktree or unpushed main receives `RELEASE_SOURCE_MAINLINE_REQUIRED` / `RELEASE_SOURCE_MAINLINE_NOT_PUSHED`; merge, test, push, and start the one mainline release instead of forcing it. Optional `.git` remote suffix differences are aliases of the same repository; genuinely different remotes still fail closed.
121
+ `release begin --change` freezes a clean committed `HEAD` only when the current branch is the authoritative default `main`/`master` and its commit exactly equals the live remote tip. Before any live write, the CLI preflights the complete target set and rejects source changes during the release. A feature worktree or unpushed main receives `RELEASE_SOURCE_MAINLINE_REQUIRED` / `RELEASE_SOURCE_MAINLINE_NOT_PUSHED`; merge, test, push, and start the one mainline release instead of forcing it. Optional `.git` remote suffix differences and other recorded repository aliases are accepted only when the frozen source-base ID intersects `releaseSourceRevision.repoAliases`; Backend, Workflow, and Root App Release writes then use the frozen canonical ID. Genuinely different identity sets still fail closed before prepare.
122
122
 
123
123
  Because promotion begins from the already-pushed authoritative mainline, `release integration-status` should pass immediately after activation. Run it, then `release end`; no post-release branch merge is required.
124
124
 
@@ -63,6 +63,8 @@ Function/Automation source analysis also extracts statically declared Form filte
63
63
 
64
64
  Source-triggered Function/Automation targets use Backend Release v2 when the platform exposes that capability. One child may mix source-backed create, source-free declarative Automation manifest create, source-only update, and manifest replacement update through explicit per-resource `operation/mode`; the CLI freezes the current Backend Release parent plus Git/change baseline, then runs `prepare -> verify -> activate` or stops verified for `--stage-only`. A new Automation with a complete `definitionJson.version="v3"` and no `sourceFile` automatically uses manifest create without `--replace-manifest`; an incomplete definition still fails closed. Activation CAS-checks the entire set and applies all updates in one transaction. `--stage-only` and Secret-bound Function publishing fail closed when Backend Release v2 is unavailable; compatibility fallback is limited to non-staged, non-Secret publishing after an explicit Backend head 404. Existing online bindings, contracts, metadata, trigger/view configuration, and enabled/published state remain unchanged; noops do not advance versions/timestamps. A deliberate whole-definition replacement of an existing resource requires exact `--only/--code` and `--replace-manifest --reason "<why>"`; SDD bypass does not imply replacement authority.
65
65
 
66
+ Repository identity remains strict across immutable releases. If a clone-derived primary repository ID differs from the frozen change source base, the CLI may canonicalize Backend, Workflow, and Root App Release payloads to the frozen ID only when that ID is already present in `releaseSourceRevision.repoAliases`; no alias intersection fails with `RELEASE_SOURCE_REPOSITORY_MISMATCH` before prepare.
67
+
66
68
  Functions with a top-level `secretRefs` field use `backend_release_v2`, including an explicit empty list that removes bindings. This path never falls back to source PATCH. It requires the per-app Secret capability probe to grant `app_function_secrets_v1`, `trusted_node_v2`, `backend_release_v2`, and `atomic_staged_children_v2`; otherwise plan/publish fails closed.
67
69
 
68
70
  For whole-app atomic activation, publish the exact mixed Function/Automation scope with `resource publish function,automation --only function:<code>,automation:<code> --stage-only --change <change>`. This stops after Backend Release verify and returns `stagedResource` plus every handled selector; the CLI automatically merges it with other changed staged children in `.openxiangda/releases/<change>/staged-resources.json` for `release app-finalize --staged-resources-json`. A Backend Release that was already activated returns `activeResource` and must not be presented as staged.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.207",
3
+ "version": "1.0.208",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -26,7 +26,7 @@ This is an OpenXiangda React SPA workspace. See [AGENTS.md](mdc:AGENTS.md) for f
26
26
  - L0 read-only/docs/tests need no SDD; L1 narrow reversible fixes record exact scope without a redundant second confirmation; schema, business Functions, Automation/Workflow, permissions, auth/public access, data writes, and runtime/config are L2/L3 full-SDD work. Live evidence/archive are post-release stages.
27
27
  - Very small copy/style/binding changes may use `openxiangda sdd quick <change> ...`; quick mode records exact low-risk scope without a redundant proposal/approval loop when the user already requested it.
28
28
  - SDD is streamlined by default: approval and exact structured scope are hard gates, while unfinished task/evidence/spec prose only warns. Configure `strictDocumentation: true` only when prose must block.
29
- - Before platform writes, run `release begin` from a clean local main/master that exactly equals the authoritative remote tip. Feature branches and unpushed mainline commits fail before any write. After activation, run `integration-status` and `release end`; no post-release merge is needed.
29
+ - Before platform writes, run `release begin` from a clean local main/master that exactly equals the authoritative remote tip. Feature branches and unpushed mainline commits fail before any write. A clone primary may be canonicalized to the frozen repository ID only when that ID is already in `repoAliases`; otherwise release prepare fails closed. After activation, run `integration-status` and `release end`; no post-release merge is needed.
30
30
  - Managed preproduction and production targets own independent app/resource/data identities and side-effect policy. Existing workspaces use `environment attach`; only an appType-matching legacy binding may seed preproduction and production starts empty. Never direct-publish or copy IDs across them; use `openxiangda studio` to inspect candidate, evidence, and drift state. Only an explicitly authorized commissioning reclassification may use `environment swap --reason "..." --confirm-production`; it preserves app data/Release Heads and keeps side effects restricted by default.
31
31
  - Change one target's side-effect policy only with `environment policy update <preproduction|production> --side-effect-policy-json <JSON|file> --reason "..."`: run `--dry-run` first, add `--confirm-production` for production, and never use `environment swap` for this. Patch mode validates only supplied fields and preserves unrecognized historical fields; `--full-replace` validates the supplied complete target and intentionally removes omissions. `organizationWrites=explicit_capability_only` removes the environment deny but still requires `app:organization:manage`. Keep release hard gates to explicit scope/profile/target, authorization, clean pushed mainline, immutable identity, CAS/lease, and production confirmation; prose and human acceptance notes are advisory unless strict mode is configured.
32
32
  - Account/role/permission/RBAC/organization-account/query-param authorization work must run `openxiangda design gates --topic permissions --json`, choose `managed-platform-account`, `existing-platform-user-assignment`, `static-role-permission`, or `query-param-context`, and write the permission matrix.
@@ -32,7 +32,7 @@ This is an OpenXiangda React SPA workspace. Read [AGENTS.md](AGENTS.md) for full
32
32
  - Function/Automation 源码触发默认走服务端 source-field PATCH,保留线上 bindings/contracts/metadata/trigger/view/enabled/published state;无源码且 `definitionJson.version="v3"` 完整的新建 Automation 自动走 manifest create,只有替换已有整包 manifest 才必须加 `--replace-manifest --reason "..."`。
33
33
  - 相同 change 的 staged FormRelease 只有经服务端重新核验 immutable/inactive/non-aborted、identity/hash、冻结 schema/formType、finalized 资源、parent/base revision 与当前 Form Head 后,才可重挂接新 baseline/session。`schemaSyncedAt` 不是发布证据;禁止伪造、直发 schema 或提前激活 Form 绕过 Workflow 校验。
34
34
  - 环境托管 `release ship --replace-manifest --reason "..."` 必须成对、reason 至少 8 字符且仅限精确 Backend selector;正式确认复用与预发完全相同的参数,不透传 Form/Workflow/Runtime/配置或全量步骤。
35
- - Promotion 必须持有 `release begin/end` 租约;`release begin` 只接受与权威远端 tip 完全一致的 clean main/master。feature branch 或未 push 主线在任何写入前失败。激活后直接运行 `integration-status` 和 `release end`,不再补做发布后合并。
35
+ - Promotion 必须持有 `release begin/end` 租约;`release begin` 只接受与权威远端 tip 完全一致的 clean main/master。feature branch 或未 push 主线在任何写入前失败。clone primary 只有在冻结仓库 ID 已存在于 `repoAliases` 时才会 canonicalize;无交集在 Release prepare 前失败关闭。激活后直接运行 `integration-status` 和 `release end`,不再补做发布后合并。
36
36
  - 已有工作区通过 `environment attach` 接入环境组,旧资源映射只迁移到 appType 相同的预发 target,正式 target 必须为空。preproduction / production 分别拥有独立 appType、资源 ID、数据和副作用策略;禁止直接 `release publish`,禁止跨环境复制 ID,使用 `openxiangda studio` 查看状态。只有用户明确授权的投产前重分类可执行 `environment swap --reason "..." --confirm-production`;它不移动应用数据或 Release Head,且默认不放开副作用。
37
37
  - 单独修改某个环境的副作用策略只能使用 `environment policy update <preproduction|production> --side-effect-policy-json <JSON|file> --reason "..."`:先执行 `--dry-run`,正式环境额外要求 `--confirm-production`,不得借用 `environment swap`。patch 只校验本次提交字段并原样保留未知历史字段,`--full-replace` 才按完整目标删除遗漏字段。`organizationWrites=explicit_capability_only` 只解除环境级 deny,仍强制 `app:organization:manage`。发布硬门禁只保留明确 scope/profile/target、权限、干净且已推送主线、不可变版本、CAS/租约与生产确认;文案和人工验收说明默认是建议,只有显式 strict 模式才阻断。
38
38
  - React SPA 路由由 `src/app/router.tsx` 管理,前端包通过 `openxiangda runtime deploy` 发布。
@@ -82,7 +82,7 @@ App Function 第三方凭据只能在 Function manifest 顶层声明 `secretRefs
82
82
 
83
83
  `openxiangda runtime deploy --no-activate` 会构建并上传不可变预览版本;发布前先提交所有可能进入构建的源码/配置。所有 Runtime deploy(包括 `--no-activate`)都会先获取应用发布 lease,并在任何构建和上传前冻结 clean `HEAD` 与当前 active Runtime 父血缘;旧分支返回 `RUNTIME_SOURCE_BASE_DIVERGED`,不能先上传旧 preview 再激活。`openspec/` SDD 证据和生成/状态目录不算源码 dirty。仅审批的回退可使用 `--allow-runtime-rollback --reason "至少 8 个字符"`;`--no-build` 不会跳过守卫。不要手工修改 `dist/index.html`。
84
84
 
85
- Function/Automation 走 Backend Release v2;同一个 child 可以混合源码 create、无 `sourceFile` 的完整 v3 声明式 Automation manifest create、source-only update 与显式 manifest replacement,并对整个集合做 CAS。声明式 create 自动选路且不需要 `--replace-manifest`;替换已有资源才需要该显式授权。正式多资源发布必须使用 canonical 精确 selector 和 `--stage-only`。`release begin` 只接受与权威远端默认主分支完全一致的 clean HEAD;feature branch 或未 push 的 main 会在任何平台写入前失败。成功激活后主线证据天然成立,不再补做发布后合并。
85
+ Function/Automation 走 Backend Release v2;同一个 child 可以混合源码 create、无 `sourceFile` 的完整 v3 声明式 Automation manifest create、source-only update 与显式 manifest replacement,并对整个集合做 CAS。声明式 create 自动选路且不需要 `--replace-manifest`;替换已有资源才需要该显式授权。正式多资源发布必须使用 canonical 精确 selector 和 `--stage-only`。`release begin` 只接受与权威远端默认主分支完全一致的 clean HEAD;feature branch 或未 push 的 main 会在任何平台写入前失败。clone primary 与冻结仓库 ID 不同时,只有该冻结 ID 已存在于 `repoAliases` 才会统一用于 Backend/Workflow/Root App Release;无交集继续失败关闭。成功激活后主线证据天然成立,不再补做发布后合并。
86
86
 
87
87
  ## 应用结构
88
88
 
@@ -32,7 +32,7 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. See [AGEN
32
32
  - Source-triggered Function/Automation publishing uses server-side source-field PATCH by default and preserves online bindings, contracts, metadata, trigger/view configuration, and enabled/published state. A new source-free Automation with a complete `definitionJson.version="v3"` automatically uses manifest create; replacing an existing whole manifest requires exact `--only/--code` plus `--replace-manifest --reason "..."`.
33
33
  - A staged FormRelease for the same change may be rebound to a fresh baseline/session only after server verification of immutable/inactive/non-aborted state, identity/hash, frozen schema/formType, finalized resources, parent/base revision, and current Form head. `schemaSyncedAt` is not release evidence; never synthesize it, direct-publish the schema, or activate the Form early to bypass Workflow validation.
34
34
  - Managed `release ship --replace-manifest --reason "..."` requires the pair, reason length >= 8, and exact Backend selectors; production confirmation repeats the exact preproduction pair, with no forwarding to Form/Workflow/Runtime/config/all stages.
35
- - Before platform writes, run `release begin` only from clean main/master exactly equal to the authoritative remote tip. Feature branches and unpushed mainline commits fail before writes. After activation, run `integration-status` and `release end`; no post-release merge is needed.
35
+ - Before platform writes, run `release begin` only from clean main/master exactly equal to the authoritative remote tip. Feature branches and unpushed mainline commits fail before writes. A clone primary may be canonicalized to the frozen repository ID only when that ID is already in `repoAliases`; otherwise release prepare fails closed. After activation, run `integration-status` and `release end`; no post-release merge is needed.
36
36
  - Environment-managed workspaces never publish directly. Existing workspaces use `environment attach`; only an appType-matching legacy binding may seed preproduction and production starts empty. Run `release candidate`, `release deploy --environment preproduction`, `release test`, then promote that same candidate with `release promote --environment production --confirm-production`. Keep app/resource/data IDs isolated and inspect state with `openxiangda studio`. Only an explicitly authorized commissioning reclassification may use `environment swap --reason "..." --confirm-production`; it preserves app data/Release Heads and keeps side effects restricted by default.
37
37
  - Change one target's side-effect policy only with `environment policy update <preproduction|production> --side-effect-policy-json <JSON|file> --reason "..."`: run `--dry-run` first, add `--confirm-production` for production, and never use `environment swap` for this. Patch mode validates only supplied fields and preserves unrecognized historical fields; `--full-replace` validates the supplied complete target and intentionally removes omissions. `organizationWrites=explicit_capability_only` removes the environment deny but still requires `app:organization:manage`. Keep release hard gates to explicit scope/profile/target, authorization, clean pushed mainline, immutable identity, CAS/lease, and production confirmation; prose and human acceptance notes are advisory unless strict mode is configured.
38
38
  - Routine edits should plan and publish exact change targets: `workspace plan --profile <name> --change <change> --changed`, then `workspace publish --profile <name> --change <change> --only pages/a,forms/b --dry-run`.
@@ -32,7 +32,7 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. Read [AGE
32
32
  - Function/Automation 源码触发默认走服务端 source-field PATCH,保留线上 bindings/contracts/metadata/trigger/view/enabled/published state;无源码且 `definitionJson.version="v3"` 完整的新建 Automation 自动走 manifest create,只有替换已有整包 manifest 才必须精确 `--only/--code` 并加 `--replace-manifest --reason "..."`。
33
33
  - 相同 change 的 staged FormRelease 只有经服务端重新核验 immutable/inactive/non-aborted、identity/hash、冻结 schema/formType、finalized 资源、parent/base revision 与当前 Form Head 后,才可重挂接新 baseline/session。`schemaSyncedAt` 不是发布证据;禁止伪造、直发 schema 或提前激活 Form 绕过 Workflow 校验。
34
34
  - 环境托管 `release ship --replace-manifest --reason "..."` 必须成对、reason 至少 8 字符且仅限精确 Backend selector;正式确认复用与预发完全相同的参数,不透传 Form/Workflow/Runtime/配置或全量步骤。
35
- - 写入平台前只从与权威远端 tip 完全一致的 clean main/master 执行 `release begin`。feature branch 或未 push 主线在任何写入前失败;激活后直接运行 `integration-status` 和 `release end`,无需发布后再合并。
35
+ - 写入平台前只从与权威远端 tip 完全一致的 clean main/master 执行 `release begin`。feature branch 或未 push 主线在任何写入前失败;clone primary 只有在冻结仓库 ID 已存在于 `repoAliases` 时才会 canonicalize,无交集在 Release prepare 前失败关闭;激活后直接运行 `integration-status` 和 `release end`,无需发布后再合并。
36
36
  - 环境托管工作区禁止直发:已有工作区先用 `environment attach` 接入,旧映射只能迁移到 appType 相同的预发 target,正式 target 必须为空。然后执行 `release candidate`、`release deploy --environment preproduction`、`release test`,最后将同一 candidate 用 `release promote --environment production --confirm-production` 晋级。两套环境的 appType、资源 ID 和数据不可互拷;用 `openxiangda studio` 查看状态。只有用户明确授权的投产前重分类可执行 `environment swap --reason "..." --confirm-production`;它不移动应用数据或 Release Head,且默认不放开副作用。
37
37
  - 单独修改某个环境的副作用策略只能使用 `environment policy update <preproduction|production> --side-effect-policy-json <JSON|file> --reason "..."`:先执行 `--dry-run`,正式环境额外要求 `--confirm-production`,不得借用 `environment swap`。patch 只校验本次提交字段并原样保留未知历史字段,`--full-replace` 才按完整目标删除遗漏字段。`organizationWrites=explicit_capability_only` 只解除环境级 deny,仍强制 `app:organization:manage`。发布硬门禁只保留明确 scope/profile/target、权限、干净且已推送主线、不可变版本、CAS/租约与生产确认;文案和人工验收说明默认是建议,只有显式 strict 模式才阻断。
38
38
  - 单文件改动默认按 change 和逻辑目标发布:先 `workspace plan --profile <name> --change <change> --changed`,再 `workspace publish --profile <name> --change <change> --only pages/a,forms/b --dry-run`。
@@ -55,7 +55,7 @@
55
55
  - ✅ 发现平台缺陷、能力缺口、规则不清、反复 workaround、AI 不确定点、用户可见体验问题时,主动 `openxiangda feedback submit --yes`;发送后告诉用户反馈内容和 fingerprint。
56
56
  - ✅ 正式多资源开发优先写 `src/resources/**` 后执行 `openxiangda resource validate|plan|publish <type> --only <codes>`;单资源可用 `--code <code>`。直接 CLI 写平台资源时先 `--dry-run`,需要避免漂移就加 `--write-manifest`。
57
57
  - ✅ `resource plan` 与 publish dry-run 严格只允许 GET/HEAD;遇到 `READ_ONLY_AUTH_REQUIRED` 时先执行 `openxiangda auth refresh --profile <name>` 或重新登录,不得在 plan 内自动 POST 刷新 token。
58
- - ✅ Function/Automation 使用 Backend Release v2;正式多资源发布用精确 `--only/--code` 加 `--stage-only` 暂存,同一 child 可混合源码 create、无 `sourceFile` 的完整 v3 声明式 Automation manifest create、source-only update 和显式 manifest replacement,再由 Root App finalize 原子激活。声明式 create 自动选路;替换已有资源的整包 manifest 才需要另加 `--replace-manifest --reason "..."`。
58
+ - ✅ Function/Automation 使用 Backend Release v2;正式多资源发布用精确 `--only/--code` 加 `--stage-only` 暂存,同一 child 可混合源码 create、无 `sourceFile` 的完整 v3 声明式 Automation manifest create、source-only update 和显式 manifest replacement,再由 Root App finalize 原子激活。声明式 create 自动选路;替换已有资源的整包 manifest 才需要另加 `--replace-manifest --reason "..."`。clone primary 与冻结仓库 ID 不同时,只有冻结 ID 已存在于 `repoAliases` 才会统一用于 Backend/Workflow/Root App Release;无交集继续失败关闭。
59
59
  - ✅ 相同 change 已有 staged FormRelease 时,不要直发 schema、伪造 `schemaSyncedAt` 或提前激活 Form。CLI 只在重新核验服务端不可变状态、identity/hash、冻结 schema/formType、finalized 资源、parent/base revision 与当前 Form Head 后,才将 child 重挂接到新的 baseline/session;冲突继续失败关闭。
60
60
  - ✅ 未登记环境的旧工作区,正式 promotion 先聚合 mainline bundle 并 commit/push,再运行 `release publish --change <id> --profile <name>`。
61
61
  - ✅ 已通过 `environment init` 登记或 `environment attach` 接入的工作区默认使用两段式 `release ship`。第一次命令只冻结 candidate 并部署 preproduction,停止等待正式晋级确认;确认预发结果后,另一次命令提供 `--confirm-production`,即可晋级同一 candidate。人工验收是默认建议,可用可选的 `--acceptance-note` 留痕,但不是所有低风险或紧急发布的硬审批门禁。preproduction / production 的 appType、资源 ID、数据和副作用策略完全隔离,严禁跨环境复制 ID 或直接 `release publish`。旧单目标映射只允许按相同 appType 迁移到预发。仅在用户明确授权的投产前重分类中使用 `environment swap --reason "..." --confirm-production` 原子交换两个既有应用的环境角色;数据和 Release Head 不移动,副作用默认不放开。单环境副作用策略用 `environment policy update <kind|id> ... --dry-run` 预览后按 revision CAS 写入,禁止借用 swap;patch 只校验本次提交字段并原样保留未知历史字段,`--full-replace` 才按完整目标删除遗漏字段,正式写入需 `--confirm-production`,且 `organizationWrites=explicit_capability_only` 不绕过 `app:organization:manage`。发布硬门禁只保留明确 scope/profile/target、权限、干净且已推送主线、不可变版本、CAS/租约与生产确认;文案和人工验收说明默认是建议,只有显式 strict 模式才阻断。