openxiangda 1.0.211 → 1.0.212

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
@@ -206,7 +206,7 @@ For source-only Function/Automation changes, the final command does not reconstr
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
 
209
- Use `openxiangda release app-capture` to read the platform's transactionally consistent whole-app manifest. For a normal multi-resource release, stage the changed Runtime/Page/Backend/Form child releases; commands carrying the same `--change` atomically accumulate their canonical immutable entries in `.openxiangda/releases/<change>/staged-resources.json`. Then run `app-finalize --change <change> --staged-resources-json <JSON|file>`. The CLI overlays Runtime/Page/Backend by singleton kind and Form by `formUuid`, preserves every unmodified active child from the read-only capture, and sends the complete frozen manifest through `prepare -> verify -> activate` with `activateStagedChildren=true`. Runtime staging never rewrites local active Runtime state, and a malformed staged response fails closed. The child heads and App head therefore switch in one database transaction (`atomic_staged_children_v1`); any parent, revision, asset, or hash drift stops with zero root activation and is never refreshed or retried. An aborted Form Release is never an idempotent success: rerunning the same scoped form publish removes its aborted staged index and creates a new immutable attempt automatically, after which the whole App is retried atomically. Never work around an App failure by sequentially activating forms. `app-prepare` accepts the same overlay for a manual reviewed flow, and `app-activate <releaseId> --activate-staged-children` performs the explicit atomic activation. For a legacy workspace whose already verified immutable Root cannot pass historical baseline/session compatibility, a platform administrator may use the audited break-glass path: `app-activate <releaseId> --activate-staged-children --break-glass-adopt-verified-root --reason "..."`. It skips only workspace/baseline/source compatibility checks; a fresh lease/baseline plus Root parent CAS, exact child identity/hash/parent/head, and the atomic transaction remain mandatory. Calling `app-finalize` without an overlay remains a compatibility-only retrospective aggregation of already-active children. `app-rollback <releaseId> --change <change> --reason "..."` prepares the audited rollback manifest.
209
+ Use `openxiangda release app-capture` to read the platform's transactionally consistent whole-app manifest. For a normal multi-resource release, stage the changed Runtime/Page/Backend/Form child releases; commands carrying the same `--change` atomically accumulate their canonical immutable entries in `.openxiangda/releases/<change>/staged-resources.json`. Then run `app-finalize --change <change> --staged-resources-json <JSON|file>`. The CLI overlays Runtime/Page/Backend by singleton kind and Form by `formUuid`, preserves every unmodified active child from the read-only capture, and sends the complete frozen manifest through `prepare -> verify -> activate` with `activateStagedChildren=true`. Runtime staging never rewrites local active Runtime state, and a malformed staged response fails closed. The child heads and App head therefore switch in one database transaction (`atomic_staged_children_v1`); any parent, revision, asset, or hash drift stops with zero root activation and is never refreshed or retried. An aborted Form Release is never an idempotent success: rerunning the same scoped form publish removes its aborted staged index and creates a new immutable attempt automatically, after which the whole App is retried atomically. Never work around an App failure by sequentially activating forms. `app-prepare` accepts the same overlay for a manual reviewed flow, and `app-activate <releaseId> --activate-staged-children` performs the explicit atomic activation. For a legacy workspace whose already verified immutable Root cannot pass historical baseline/session compatibility, a platform administrator may use the audited break-glass path: `app-activate <releaseId> --activate-staged-children --break-glass-adopt-verified-root --reason "..."`. It skips only workspace/baseline/source compatibility checks; a fresh lease/baseline plus Root parent CAS, exact child identity/hash/parent/head, and the atomic transaction remain mandatory. When the operator explicitly requires an unconditional legacy recovery, `app-activate <releaseId> --force-activate-without-validation --profile <name>` sends a single activation write without reading detail/capture or requiring change, lease, baseline, source, status, parent, hash, resource-head, environment, or child-lineage checks. Authentication, the bound tenant/appType data boundary, target-row existence, database constraints, and the one database transaction remain inherent execution requirements. Calling `app-finalize` without an overlay remains a compatibility-only retrospective aggregation of already-active children. `app-rollback <releaseId> --change <change> --reason "..."` prepares the audited rollback manifest.
210
210
 
211
211
  If exact FormRelease children were already staged for the same change before a new release baseline or lease is acquired, the CLI reuses them only after checking the server's immutable, inactive, non-aborted release, exact app/form identity and content hash, frozen schema/formType, finalized resources, parent/base revision, and current Form head. Verified children are rebound to the newly owned baseline/session; local `schemaSyncedAt` is not treated as release evidence. Missing or conflicting evidence still fails closed, and neither direct schema synchronization nor early Form activation is required.
212
212
 
package/lib/cli.js CHANGED
@@ -309,7 +309,7 @@ Usage:
309
309
  openxiangda release ship|candidate|deploy|test|promote|rollback [--candidate id] [--environment target] [--replace-manifest --reason text] [--adopt-online-baseline --adoption-reason text] [--json]
310
310
  openxiangda task status --change <id> [--profile name] [--watch] [--json]
311
311
  openxiangda release backend-head|backend-list|backend-detail|backend-diff|backend-rollback|backend-abort|backend-retry [releaseId] [--profile name] [--json]
312
- openxiangda release app-capture|app-head|app-list|app-detail|app-diff|app-prepare|app-verify|app-activate|app-finalize|app-rollback|app-abort [releaseId] [--staged-resources-json <JSON|file>] [--activate-staged-children] [--break-glass-adopt-verified-root --reason text] [--profile name] [--json]
312
+ openxiangda release app-capture|app-head|app-list|app-detail|app-diff|app-prepare|app-verify|app-activate|app-finalize|app-rollback|app-abort [releaseId] [--staged-resources-json <JSON|file>] [--activate-staged-children] [--break-glass-adopt-verified-root --reason text] [--force-activate-without-validation] [--profile name] [--json]
313
313
  openxiangda env [--profile name]
314
314
  openxiangda workspace init [dir] [--name package-name] [--runtime legacy|react-spa] [--install] [--profile name --app-type APP_XXX]
315
315
  openxiangda workspace prepare [--check] [--force] [--json]
@@ -3775,7 +3775,7 @@ async function release(args) {
3775
3775
  '用法: openxiangda release publish|begin|status|explain|integration-status|renew|end [--change id] [--profile name] [--json]',
3776
3776
  ' openxiangda release ship|candidate|deploy|reconcile|test|promote|rollback [--candidate id] [--environment target] [--replace-manifest --reason text] [--adopt-online-baseline --adoption-reason text] [--confirm-production] [--json]',
3777
3777
  ' openxiangda release backend-head|backend-list|backend-detail|backend-diff|backend-rollback|backend-abort|backend-retry [releaseId] [--profile name] [--json]',
3778
- ' openxiangda release app-capture|app-head|app-list|app-detail|app-diff|app-post-commit|app-retry|app-prepare|app-verify|app-activate|app-finalize|app-rollback|app-abort [releaseId] [--staged-resources-json <JSON|file>] [--activate-staged-children] [--break-glass-adopt-verified-root --reason text] [--profile name] [--json]',
3778
+ ' openxiangda release app-capture|app-head|app-list|app-detail|app-diff|app-post-commit|app-retry|app-prepare|app-verify|app-activate|app-finalize|app-rollback|app-abort [releaseId] [--staged-resources-json <JSON|file>] [--activate-staged-children] [--break-glass-adopt-verified-root --reason text] [--force-activate-without-validation] [--profile name] [--json]',
3779
3779
  '常用流程:',
3780
3780
  ' openxiangda release ship --change <id> --profile <name>',
3781
3781
  ' openxiangda release ship --change <id> --profile <name> --adopt-online-baseline --adoption-reason "已审计目标来自多次历史发布"',
@@ -5828,6 +5828,22 @@ async function activateAppReleaseDetail(
5828
5828
  releaseId,
5829
5829
  options = {}
5830
5830
  ) {
5831
+ const body = stripUndefinedValues({
5832
+ ...(options.activateStagedChildren === true
5833
+ ? { activateStagedChildren: true }
5834
+ : {}),
5835
+ ...(options.breakGlassAdoptVerifiedRoot === true
5836
+ ? {
5837
+ breakGlassAdoptVerifiedRoot: true,
5838
+ breakGlassReason: options.breakGlassReason,
5839
+ }
5840
+ : {}),
5841
+ ...(options.forceActivateWithoutValidation === true
5842
+ ? { forceActivateWithoutValidation: true }
5843
+ : {}),
5844
+ protocolVersion: options.protocolVersion,
5845
+ ...(options.environmentControl || {}),
5846
+ });
5831
5847
  return await requestWithAuth(
5832
5848
  config,
5833
5849
  target.profileName,
@@ -5837,22 +5853,10 @@ async function activateAppReleaseDetail(
5837
5853
  ),
5838
5854
  {
5839
5855
  method: 'POST',
5840
- body: appReleaseWriteBody(
5841
- releaseContext,
5842
- stripUndefinedValues({
5843
- ...(options.activateStagedChildren === true
5844
- ? { activateStagedChildren: true }
5845
- : {}),
5846
- ...(options.breakGlassAdoptVerifiedRoot === true
5847
- ? {
5848
- breakGlassAdoptVerifiedRoot: true,
5849
- breakGlassReason: options.breakGlassReason,
5850
- }
5851
- : {}),
5852
- protocolVersion: options.protocolVersion,
5853
- ...(options.environmentControl || {}),
5854
- })
5855
- ),
5856
+ body:
5857
+ options.forceActivateWithoutValidation === true
5858
+ ? body
5859
+ : appReleaseWriteBody(releaseContext, body),
5856
5860
  }
5857
5861
  );
5858
5862
  }
@@ -6075,8 +6079,26 @@ async function runAppReleaseCommand(
6075
6079
  );
6076
6080
  } else if (subcommand === 'app-activate') {
6077
6081
  if (!releaseId) {
6078
- fail('用法: openxiangda release app-activate <releaseId> --change <id>');
6082
+ fail(
6083
+ '用法: openxiangda release app-activate <releaseId> [--change <id> | --force-activate-without-validation]'
6084
+ );
6079
6085
  }
6086
+ const forceActivateWithoutValidation =
6087
+ flags['force-activate-without-validation'] === true ||
6088
+ String(flags['force-activate-without-validation'] || '').toLowerCase() ===
6089
+ 'true';
6090
+ if (forceActivateWithoutValidation) {
6091
+ data = await activateAppReleaseDetail(
6092
+ config,
6093
+ target,
6094
+ null,
6095
+ releaseId,
6096
+ {
6097
+ activateStagedChildren: true,
6098
+ forceActivateWithoutValidation: true,
6099
+ }
6100
+ );
6101
+ } else {
6080
6102
  const breakGlassAdoptVerifiedRoot =
6081
6103
  flags['break-glass-adopt-verified-root'] === true ||
6082
6104
  String(flags['break-glass-adopt-verified-root'] || '').toLowerCase() ===
@@ -6133,6 +6155,7 @@ async function runAppReleaseCommand(
6133
6155
  environmentControl: appReleaseEnvironmentControl(flags),
6134
6156
  }
6135
6157
  );
6158
+ }
6136
6159
  } else if (subcommand === 'app-finalize') {
6137
6160
  const stagedResources = readStagedAppReleaseResources(flags);
6138
6161
  let stagedScope = null;
@@ -124,7 +124,7 @@ Because promotion begins from the already-pushed authoritative mainline, `releas
124
124
 
125
125
  `runtime deploy` separately freezes the clean committed `HEAD` as `sourceRevision` together with the current active Runtime parent before any build or upload. Any deploy whose source does not descend from the online Runtime fails with `RUNTIME_SOURCE_BASE_DIVERGED`, including `--no-activate`; this prevents staging an old preview for later activation. `--no-build` does not bypass this guard. Only an intentional audited rollback may use `--allow-runtime-rollback --reason "<at least 8 characters>"`, and that flag never permits dirty or non-Git input. SDD evidence under `openspec/` and generated/state paths do not make Runtime source dirty.
126
126
 
127
- For a whole-app release, keep changed Runtime/Page/Backend/Form children staged and run `openxiangda release app-finalize --change <change> --staged-resources-json <JSON|file> --profile <name>`. The JSON contains only changed immutable child entries; the CLI overlays Runtime/Page/Backend by singleton kind and FormRelease by `formUuid` onto one authoritative read-only capture, preserving every unmodified active child. It then performs `prepare -> verify -> activateStagedChildren=true`, switching child heads and the App head atomically (`atomic_staged_children_v1`, or `atomic_staged_children_v2` when a Backend Release v2 child is present). Never refresh or retry after a conflict. The flow requires the owned stored lease/change baseline and carries the same client session and Git lineage on every write. A platform administrator may recover a legacy, already verified immutable Root with `app-activate <releaseId> --activate-staged-children --break-glass-adopt-verified-root --reason "..."`. This audited recovery skips workspace/baseline/source compatibility only; it still requires a fresh lease/baseline and enforces Root parent CAS, exact child identity/hash/parent/head, and atomic activation. Omitting the overlay is compatibility-only retrospective aggregation of already-active children.
127
+ For a whole-app release, keep changed Runtime/Page/Backend/Form children staged and run `openxiangda release app-finalize --change <change> --staged-resources-json <JSON|file> --profile <name>`. The JSON contains only changed immutable child entries; the CLI overlays Runtime/Page/Backend by singleton kind and FormRelease by `formUuid` onto one authoritative read-only capture, preserving every unmodified active child. It then performs `prepare -> verify -> activateStagedChildren=true`, switching child heads and the App head atomically (`atomic_staged_children_v1`, or `atomic_staged_children_v2` when a Backend Release v2 child is present). Never refresh or retry after a conflict. The flow requires the owned stored lease/change baseline and carries the same client session and Git lineage on every write. A platform administrator may recover a legacy, already verified immutable Root with `app-activate <releaseId> --activate-staged-children --break-glass-adopt-verified-root --reason "..."`. This audited recovery skips workspace/baseline/source compatibility only; it still requires a fresh lease/baseline and enforces Root parent CAS, exact child identity/hash/parent/head, and atomic activation. An explicitly authorized unconditional legacy recovery may use `app-activate <releaseId> --force-activate-without-validation --profile <name>`; this sends one activation write without local reads or release control/lineage/CAS gates. Authentication, bound tenant/appType scope, target-row existence, database constraints, and transactional execution remain inherent. Omitting the overlay is compatibility-only retrospective aggregation of already-active children.
128
128
 
129
129
  When the same change already has staged FormRelease children before a fresh baseline/lease is acquired, keep them staged. The CLI may rebind each child into the new session only after server verification of immutable/inactive/non-aborted state, exact app/form identity and content hash, frozen schema/formType, finalized resources, parent/base revision, and current Form head. `schemaSyncedAt` is local cache metadata, not release evidence. Do not direct-publish or activate a Form to bypass Workflow validation; missing or conflicting staged evidence must fail closed.
130
130
 
@@ -73,7 +73,7 @@ Standard workspaces use the CLI-bundled scoped JS_CODE builder directly, includi
73
73
 
74
74
  Use `openxiangda release backend-head|backend-list|backend-detail|backend-diff` to inspect immutable history. `backend-rollback <releaseId> --change <change> --reason "..."` prepares, verifies, and activates a new release from the historical snapshot; it never mutates or directly reactivates the old row. `backend-abort` stops prepared/verified work and `backend-retry` retries only recorded post-commit side effects. Do not fetch newer revisions to replay an older payload.
75
75
 
76
- Use `openxiangda release app-capture` for the platform's authoritative transactionally consistent whole-app manifest. Normal publishing stages changed Runtime/Page/Backend/Form children first, then runs `app-finalize --change <change> --staged-resources-json <JSON|file>`. The overlay contains only changed immutable child entries; Runtime/Page/Backend use singleton kind identity and FormRelease uses `formUuid`. The CLI preserves unmodified active children from the capture and performs `prepare -> verify -> activateStagedChildren=true`, atomically switching all child heads and the App head (`atomic_staged_children_v1`). Child drift or a lost lease causes zero root activation; never auto-refresh or conflict-retry. An aborted Form Release must never be reused as an idempotent result. Rerun the same exact form resource publish: the CLI discards the aborted staged entry, the platform creates a new immutable attempt for the same logical artifact, and the Root App transaction is retried. Never sequentially activate forms as a workaround. `app-prepare` accepts the same overlay for manual review, and `app-activate <releaseId> --activate-staged-children` performs the explicit transaction switch. A platform administrator may recover a legacy, already verified immutable Root with `app-activate <releaseId> --activate-staged-children --break-glass-adopt-verified-root --reason "..."`. This audited path skips only workspace/baseline/source compatibility checks; it still requires a fresh lease/baseline and enforces Root parent CAS, exact child identity/hash/parent/head, and atomic activation. No-overlay finalize remains retrospective compatibility only. `app-rollback <releaseId> --change <change> --reason "..."` prepares an audited rollback manifest.
76
+ Use `openxiangda release app-capture` for the platform's authoritative transactionally consistent whole-app manifest. Normal publishing stages changed Runtime/Page/Backend/Form children first, then runs `app-finalize --change <change> --staged-resources-json <JSON|file>`. The overlay contains only changed immutable child entries; Runtime/Page/Backend use singleton kind identity and FormRelease uses `formUuid`. The CLI preserves unmodified active children from the capture and performs `prepare -> verify -> activateStagedChildren=true`, atomically switching all child heads and the App head (`atomic_staged_children_v1`). Child drift or a lost lease causes zero root activation; never auto-refresh or conflict-retry. An aborted Form Release must never be reused as an idempotent result. Rerun the same exact form resource publish: the CLI discards the aborted staged entry, the platform creates a new immutable attempt automatically, and the Root App transaction is retried. Never sequentially activate forms as a workaround. `app-prepare` accepts the same overlay for manual review, and `app-activate <releaseId> --activate-staged-children` performs the explicit transaction switch. A platform administrator may recover a legacy, already verified immutable Root with `app-activate <releaseId> --activate-staged-children --break-glass-adopt-verified-root --reason "..."`. This audited path skips only workspace/baseline/source compatibility checks; it still requires a fresh lease/baseline and enforces Root parent CAS, exact child identity/hash/parent/head, and atomic activation. For an explicitly authorized unconditional legacy recovery, `app-activate <releaseId> --force-activate-without-validation --profile <name>` performs one direct activation write without detail/capture/change/lease/baseline/source/status/parent/hash/resource-head/environment/child-lineage gates; only authentication, the bound tenant/appType data boundary, target-row existence, database constraints, and the transaction remain inherent. No-overlay finalize remains retrospective compatibility only. `app-rollback <releaseId> --change <change> --reason "..."` prepares an audited rollback manifest.
77
77
 
78
78
  ## Classic workspace
79
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.211",
3
+ "version": "1.0.212",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -58,7 +58,7 @@
58
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
- - ✅ 旧工作区已有 `verified + immutable` Root、但历史 baseline/session 兼容校验无法恢复时,只允许平台管理员显式执行 `release app-activate <releaseId> --activate-staged-children --break-glass-adopt-verified-root --reason "..."`。该审计入口仍要求新租约/基线、相同 tenant/app/holder/change,并保留 Root parent CAS、child identity/hash/parent/head 与原子事务门禁。
61
+ - ✅ 旧工作区已有 Root、且操作者明确授权无条件恢复时,可执行 `release app-activate <releaseId> --force-activate-without-validation --profile <name>`。该命令不读取 detail/capture,不要求 change、租约、baseline、源码 lineage、状态、parent、hash、resource head 或环境发布门禁;服务端直接在目标 tenant/appType 内以单事务切换 Root 与可识别的 staged children。
62
62
  - ✅ 已通过 `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 模式才阻断。
63
63
  - ✅ 只有已审计目标早已进入权威主线、线上却由多次历史 lineage 组成且无法对应单一 Git 基线时,第一次 `release ship` 才可增加 `--adopt-online-baseline --adoption-reason "..."`;该意图冻结进私有 `ship.json` 并由后续 `--confirm-production` 自动复用。仅允许精确非删除 selectors,冻结 Head、change/lease、服务端 CAS、staged children 与单次 App finalize 仍是硬门禁。
64
64
  - ✅ 环境托管发布需要完整替换 Function/Automation manifest 时,`release ship` 可成对增加 `--replace-manifest --reason "..."`;reason 至少 8 字符,只透传精确 Backend selector,正式确认必须复用同一对参数,不扩散到 Form/Workflow/Runtime/配置或全量范围。