openxiangda 1.0.254 → 1.0.255

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
@@ -132,6 +132,8 @@ DataView `status` is a last-observed lifecycle value, not a stable candidate bin
132
132
 
133
133
  Exact, non-destructive configuration selectors such as Data Views and permission groups are now sequenced automatically inside the same ship journal instead of requiring separate SDD changes. New forms are idempotently ensured per environment before their immutable FormRelease is staged. Unscoped resources and destructive configuration deletes remain fail-closed.
134
134
 
135
+ When a release contains both Form settings and form permission groups, `sdd bundle` emits one atomic `resource publish form-setting,form-permission-group` command with qualified `form-setting:<code>` and `form-permission-group:<code>` selectors. The prepublish verifier applies that same canonical contract, requires exact coverage of both sets, and still rejects missing, extra, split, unscoped, or directly activated Form resources.
136
+
135
137
  Existing workspaces connect to a server-side environment set with `environment attach`. If the legacy `profiles.<profile>` binding has the same `appType` as one environment, its resource mappings are copied only into that matching target (normally preproduction). Production starts with an empty mapping, and later writes update only the selected target even when both targets reuse one login profile.
136
138
 
137
139
  `environment swap` is a commissioning/reclassification operation, not a release shortcut. It requires `--confirm-production` and a reason, atomically exchanges the two existing environment roles, preserves each application's data and Release Heads, and remaps local resource IDs by appType. Existing side-effect policies remain restricted unless explicit replacement policy JSON is supplied.
package/lib/sdd.js CHANGED
@@ -2589,12 +2589,30 @@ function validateAtomicReleaseCommands(
2589
2589
  const errors = [];
2590
2590
  const requireCompletePlan = options.requireCompletePlan !== false;
2591
2591
  const normalized = commands.map(command => String(command || '').trim());
2592
+ const expectedFormPermissionGroups =
2593
+ targets.resourceSelectors?.formPermissionGroups || [];
2594
+ const expectedFormTypes = [
2595
+ ...((targets.forms || []).length > 0 ? ['form-setting'] : []),
2596
+ ...(expectedFormPermissionGroups.length > 0
2597
+ ? ['form-permission-group']
2598
+ : []),
2599
+ ].sort();
2600
+ const qualifyFormSelectors = expectedFormTypes.length > 1;
2601
+ const expectedFormSelectors = [
2602
+ ...(targets.forms || []).map(code =>
2603
+ qualifyFormSelectors ? `form-setting:${code}` : String(code)
2604
+ ),
2605
+ ...expectedFormPermissionGroups.map(code =>
2606
+ qualifyFormSelectors ? `form-permission-group:${code}` : String(code)
2607
+ ),
2608
+ ].sort();
2592
2609
  const publishCommands = normalized.filter(command =>
2593
2610
  /^openxiangda\s+(?:workspace\s+publish|resource\s+publish|runtime\s+deploy|release\s+app-finalize)\b/.test(
2594
2611
  command
2595
2612
  )
2596
2613
  );
2597
2614
  for (const command of publishCommands) {
2615
+ const commandTypes = normalizedResourcePublishTypes(command);
2598
2616
  if (!/\s--profile(?:=|\s+)\S+/.test(command) || /<profile>/.test(command)) {
2599
2617
  errors.push({
2600
2618
  name: 'sdd-release-profile-missing',
@@ -2619,7 +2637,9 @@ function validateAtomicReleaseCommands(
2619
2637
  });
2620
2638
  }
2621
2639
  if (
2622
- /^openxiangda\s+resource\s+publish\s+form-setting\b/.test(command) &&
2640
+ commandTypes.some(type =>
2641
+ ['form-setting', 'form-permission-group'].includes(type)
2642
+ ) &&
2623
2643
  /\s--activate(?:\s|$)/.test(command)
2624
2644
  ) {
2625
2645
  errors.push({
@@ -2645,7 +2665,6 @@ function validateAtomicReleaseCommands(
2645
2665
  message: `Runtime 必须先 --no-activate 暂存,再由 App Release 原子激活: ${command}`,
2646
2666
  });
2647
2667
  }
2648
- const commandTypes = normalizedResourcePublishTypes(command);
2649
2668
  const expectedBackendTypes = [
2650
2669
  ...((targets.functions || []).length > 0 ? ['function'] : []),
2651
2670
  ...((targets.automations || []).length > 0 ? ['automation'] : []),
@@ -2680,16 +2699,18 @@ function validateAtomicReleaseCommands(
2680
2699
  }
2681
2700
  }
2682
2701
  if (
2683
- commandTypes.includes('form-setting') &&
2684
- (targets.forms || []).length > 0 &&
2685
- !sameStringSet(
2686
- normalizedCommandSelectors(command),
2687
- (targets.forms || []).map(String)
2688
- )
2702
+ commandTypes.some(type =>
2703
+ ['form-setting', 'form-permission-group'].includes(type)
2704
+ ) &&
2705
+ (!sameStringSet(commandTypes, expectedFormTypes) ||
2706
+ !sameStringSet(
2707
+ normalizedCommandSelectors(command),
2708
+ expectedFormSelectors
2709
+ ))
2689
2710
  ) {
2690
2711
  errors.push({
2691
2712
  name: 'sdd-release-form-scope-inexact',
2692
- message: `Form bundle 命令必须精确覆盖审核 selector,不能缺失或夹带表单: ${command}`,
2713
+ message: `Form bundle 命令必须以一条命令精确覆盖审核的 Form 与权限组 selector,不能缺失或夹带资源: ${command}`,
2693
2714
  });
2694
2715
  }
2695
2716
  }
@@ -2729,20 +2750,21 @@ function validateAtomicReleaseCommands(
2729
2750
  });
2730
2751
  }
2731
2752
  }
2732
- if ((targets.forms || []).length > 0) {
2753
+ if (expectedFormTypes.length > 0) {
2733
2754
  const formStage = normalized.find(
2734
2755
  command =>
2735
- /^openxiangda\s+resource\s+publish\s+form-setting\b/.test(command) &&
2756
+ /^openxiangda\s+resource\s+publish\b/.test(command) &&
2736
2757
  !/\s--activate(?:\s|$)/.test(command) &&
2737
2758
  sameStringSet(
2738
- normalizedCommandSelectors(command),
2739
- (targets.forms || []).map(String)
2740
- )
2759
+ normalizedResourcePublishTypes(command),
2760
+ expectedFormTypes
2761
+ ) &&
2762
+ sameStringSet(normalizedCommandSelectors(command), expectedFormSelectors)
2741
2763
  );
2742
2764
  if (!formStage) {
2743
2765
  errors.push({
2744
2766
  name: 'sdd-release-form-stage-missing',
2745
- message: 'Form 必须由精确 form-setting bundle 命令暂存为 FormRelease。',
2767
+ message: 'Form 与权限组必须由一条精确 bundle 命令暂存为 FormRelease。',
2746
2768
  });
2747
2769
  }
2748
2770
  }
@@ -2761,7 +2783,7 @@ function validateAtomicReleaseCommands(
2761
2783
  }
2762
2784
  if (
2763
2785
  backendCodes.length > 0 ||
2764
- (targets.forms || []).length > 0 ||
2786
+ expectedFormTypes.length > 0 ||
2765
2787
  targets.runtime
2766
2788
  ) {
2767
2789
  const finalize = normalized.find(
@@ -107,7 +107,7 @@ Quick changes use compact generated JSON/prose. Do not expand them into proposal
107
107
 
108
108
  Keep development lightweight: structured approval plus exact resource/file scope are authoritative. By default, only `change.json`, `coverage.json`, and `release.json` are created; use `openxiangda sdd render <change>` when prose is genuinely useful. Missing tasks/evidence/spec prose warns and does not block release; set `strictDocumentation: true` only for a workspace that intentionally wants prose as a gate. Actual publish argv, mainline identity, CAS, lease, destructive scope, and atomic activation remain hard gates.
109
109
 
110
- The CLI regenerates one canonical command set from structured coverage instead of asking agents to maintain two command copies. A single Function uses `resource publish function --only <code>`; qualified selectors are used only when Function and Automation are mixed. Generated React SPA plans use exact Form bundles, one Backend `--stage-only` command, Runtime `--no-activate`, and Root `app-finalize`. Actual argv is validated again at write time.
110
+ The CLI regenerates one canonical command set from structured coverage instead of asking agents to maintain two command copies. A single Function uses `resource publish function --only <code>`; qualified selectors are used when Function and Automation are mixed. When Form settings and form permission groups are both present, they must share one `resource publish form-setting,form-permission-group` command with qualified `form-setting:<code>` and `form-permission-group:<code>` selectors so one immutable FormRelease contains both snapshots. Generated React SPA plans use exact Form bundles, one Backend `--stage-only` command, Runtime `--no-activate`, and Root `app-finalize`. Prepublish and actual-argv validation require those exact same type/selector sets and reject missing, extra, or split Form resources.
111
111
 
112
112
  ## Safe release recipes
113
113
 
@@ -77,6 +77,8 @@ The approved change scope, not the checkout's global dirty set, is the release b
77
77
 
78
78
  Treat `scopes.changedResources`, `scopes.runtimeDependencies`, and `scopes.deployTargets` as different contracts. The first owns changed files/resources, the second records shared/transitive impact, and only the third authorizes writes. Dependency analysis may fail closed when an impact was not declared, but it must not silently expand `deployTargets`. Candidate preflight runs release-plan and SDD prepublish checks before source freeze or remote candidate/deployment writes; failure leaves reviewed SDD and private execution state untouched.
79
79
 
80
+ For React SPA releases that contain both Form settings and form permission groups, keep them in one immutable FormRelease command: `resource publish form-setting,form-permission-group --only form-setting:<form>,form-permission-group:<group>`. The SDD generator and prepublish/actual-argv validators use this same canonical qualified-selector contract; missing, extra, split, unscoped, or directly activated Form resources remain blocked.
81
+
80
82
  For engineering resources, select type and exact codes:
81
83
 
82
84
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.254",
3
+ "version": "1.0.255",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -40,6 +40,7 @@ This is an OpenXiangda React SPA workspace using Delivery V2. See [DELIVERY.md](
40
40
  - Select logical resource codes with `--only`, or one code with `--code`; type-wide/app-wide release requires an approved dependency closure.
41
41
  - Source-triggered Function/Automation publishing uses server-side source-field PATCH 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 Backend Release manifest create without `--replace-manifest`; replacing an existing whole manifest requires `--replace-manifest --reason "..."`. On `SOURCE_BASE_DIVERGED` or `RESOURCE_FIELD_CONFLICT`, reconcile, rebuild, and re-plan.
42
42
  - 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.
43
+ - When Form settings and form permission groups ship together, stage them with one `resource publish form-setting,form-permission-group` command and exact qualified `form-setting:<code>` / `form-permission-group:<code>` selectors. SDD generation and validation share this contract and reject missing, extra, or split FormRelease scope.
43
44
  - Managed `release ship --adopt-online-baseline --adoption-reason "..."` freezes that audited intent in the private ship journal; the later `--confirm-production` automatically reuses it and rejects an explicitly different pair before any request.
44
45
  - 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.
45
46
  - After managed promotion, run `release integration-status --change <change> --profile <name> --check`; it recovers lineage from private `ship.json` or its referenced production/preproduction deployment execution journal, and names the missing file or field when recovery is impossible.
@@ -33,6 +33,7 @@ This is an OpenXiangda React SPA workspace using Delivery V2. Read [DELIVERY.md]
33
33
  - 默认按逻辑资源 code 使用 `--only` 或单资源 `--code`;全类型/全应用发布必须由批准的依赖闭包明确覆盖。
34
34
  - Function/Automation 源码触发默认走服务端 source-field PATCH,保留线上 bindings/contracts/metadata/trigger/view/enabled/published state;无源码且 `definitionJson.version="v3"` 完整的新建 Automation 自动走 manifest create,只有替换已有整包 manifest 才必须加 `--replace-manifest --reason "..."`。
35
35
  - 相同 change 的 staged FormRelease 只有经服务端重新核验 immutable/inactive/non-aborted、identity/hash、冻结 schema/formType、finalized 资源、parent/base revision 与当前 Form Head 后,才可重挂接新 baseline/session。`schemaSyncedAt` 不是发布证据;禁止伪造、直发 schema 或提前激活 Form 绕过 Workflow 校验。
36
+ - Form 设置与表单权限组同时发布时,必须合并为一条 `resource publish form-setting,form-permission-group`,并使用 `form-setting:<code>`、`form-permission-group:<code>` 精确 selector;SDD 生成与校验共享同一契约,禁止缺失、夹带或拆分 FormRelease。
36
37
  - 环境托管 `release ship --adopt-online-baseline --adoption-reason "..."` 会把审计意图冻结进私有 ship journal,后续 `--confirm-production` 自动复用;显式传入不同参数会在任何请求前失败。
37
38
  - 环境托管 `release ship --replace-manifest --reason "..."` 必须成对、reason 至少 8 字符且仅限精确 Backend selector;正式确认复用与预发完全相同的参数,不透传 Form/Workflow/Runtime/配置或全量步骤。
38
39
  - `release ship` 始终顺序执行 candidate → 预发 → 生产。日常分两次确认;明确授权紧急发布时首条命令可带 `--confirm-production` 一次完成,但不跳过预发/证据/CAS。candidate 封存环境/资源绑定及两目标 Runtime 哈希产物,部署不重建且每条服务端 deployment 必须闭环为 `succeeded`。
@@ -58,6 +58,8 @@ openxiangda commands --json
58
58
 
59
59
  模板已停用无范围的 `pnpm deploy` 聚合入口。日常变更必须使用 `resource plan|publish <type> --only <codes>`(单资源可用 `--code <code>`)。Form bundle、Backend Release 和 Runtime 都先暂存;CLI 会按 `--change` 自动聚合 `.openxiangda/releases/<change>/staged-resources.json`,最后只由一次 Root App finalize 原子激活。Form Release 一旦 abort 绝不能作为幂等结果复用;重新执行相同精确表单发布时,CLI 会淘汰旧 staged 索引,平台会创建新的不可变 attempt,再由 Root App 一次事务重试。相同 change 的既有 staged FormRelease 只有在 CLI 重新核验服务端不可变状态、identity/hash、冻结 schema/formType、finalized 资源、parent/base revision 和当前 Form Head 后,才可重挂接到新的 baseline/session;`schemaSyncedAt` 只是本地缓存元数据。禁止伪造它、提前激活 Form 或用顺序激活多张表单绕过失败。不要使用 `workspace publish --form`、单独 `runtime activate`、`pnpm publish:all`、`pnpm openxiangda:publish` 或 `lowcode-workspace publish-all`。
60
60
 
61
+ 同一发布同时包含 Form 设置和表单权限组时,必须由一条 `resource publish form-setting,form-permission-group` 命令暂存,并在 `--only` 中分别使用 `form-setting:<code>`、`form-permission-group:<code>`。SDD bundle 与 prepublish 校验使用同一精确契约;缺失、夹带、拆成两个 FormRelease 或提前激活都应失败关闭。
62
+
61
63
  工作区一旦通过 `environment init` 登记或 `environment attach` 接入,`release publish` 即不再是入口。`release ship` 始终按 candidate → preproduction → production 执行:日常首条命令在预发停止,后续 `--confirm-production` 晋级;用户明确授权紧急发布时,首条命令可直接携带 `--confirm-production`,在一个命令内顺序执行两阶段,但不跳过预发、证据、CAS 或确认。candidate 封存源码、public、构建配置/脚本、稳定环境/资源绑定和两目标 Runtime 哈希产物;部署不再现场构建,并将两条服务端 deployment 都闭环为 `succeeded`。两套环境身份仍完全隔离,人工验收默认建议且可用 `--acceptance-note` 留痕;swap、policy 和权限规则保持不变。`openxiangda studio` 用于查看绑定、漂移、候选、部署和证据。
62
64
 
63
65
  DataView 的 `status` 是平台生命周期观察值;同一托管部署在预发暂存和正式确认之间发生 `active → draft → active` 不构成候选漂移。候选仍严格校验 `dataViewId`、`materializedViewName`、`storageMode`、其他资源状态、哈希、环境身份、CAS、租约和来源主线。
@@ -36,6 +36,7 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. See [AGEN
36
36
  - SDD is streamlined by default: approval and exact structured scope are hard gates, while unfinished task/evidence/spec prose only warns. Use `strictDocumentation: true` only when prose must block.
37
37
  - 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 "..."`.
38
38
  - 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.
39
+ - When Form settings and form permission groups ship together, stage them with one `resource publish form-setting,form-permission-group` command and exact qualified `form-setting:<code>` / `form-permission-group:<code>` selectors. SDD generation and validation share this contract and reject missing, extra, or split FormRelease scope.
39
40
  - 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.
40
41
  - After managed promotion, run `release integration-status --change <change> --profile <name> --check`; it recovers lineage from private `ship.json` or its referenced production/preproduction deployment execution journal, and names the missing file or field when recovery is impossible.
41
42
  - 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,6 +36,7 @@ This is a `sy-lowcode-app-workspace` managed by the `openxiangda` CLI. Read [AGE
36
36
  - SDD 默认 streamlined:approval 与结构化精确范围是硬门禁,未完成的 task/evidence/spec 文案只告警;只有 `strictDocumentation: true` 才阻断。
37
37
  - 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 "..."`。
38
38
  - 相同 change 的 staged FormRelease 只有经服务端重新核验 immutable/inactive/non-aborted、identity/hash、冻结 schema/formType、finalized 资源、parent/base revision 与当前 Form Head 后,才可重挂接新 baseline/session。`schemaSyncedAt` 不是发布证据;禁止伪造、直发 schema 或提前激活 Form 绕过 Workflow 校验。
39
+ - Form 设置与表单权限组同时发布时,必须合并为一条 `resource publish form-setting,form-permission-group`,并使用 `form-setting:<code>`、`form-permission-group:<code>` 精确 selector;SDD 生成与校验共享同一契约,禁止缺失、夹带或拆分 FormRelease。
39
40
  - 环境托管 `release ship --replace-manifest --reason "..."` 必须成对、reason 至少 8 字符且仅限精确 Backend selector;正式确认复用与预发完全相同的参数,不透传 Form/Workflow/Runtime/配置或全量步骤。
40
41
  - `release ship` 始终顺序执行 candidate → 预发 → 生产。日常分两次确认;明确授权紧急发布时首条命令可带 `--confirm-production` 一次完成,但不跳过预发/证据/CAS。candidate 封存环境/资源绑定及两目标 Runtime 哈希产物,部署不重建且每条服务端 deployment 必须闭环为 `succeeded`。
41
42
  - 预发 UAT 失败使用 `release fail --deployment <id> --message "..." [--code <code>] [--details-json <JSON|file>] --environment preproduction` 写审计;CLI 先校验 deployment 属于所选预发环境,禁止 production 或环境不匹配写入。
@@ -62,6 +62,7 @@ Delivery V2 自动从期望状态按资源指纹计算精确范围,使用 CLI
62
62
  - ✅ `resource plan` 与 publish dry-run 严格只允许 GET/HEAD;遇到 `READ_ONLY_AUTH_REQUIRED` 时先执行 `openxiangda auth refresh --profile <name>` 或重新登录,不得在 plan 内自动 POST 刷新 token。
63
63
  - ✅ 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;无交集继续失败关闭。
64
64
  - ✅ 相同 change 已有 staged FormRelease 时,不要直发 schema、伪造 `schemaSyncedAt` 或提前激活 Form。CLI 只在重新核验服务端不可变状态、identity/hash、冻结 schema/formType、finalized 资源、parent/base revision 与当前 Form Head 后,才将 child 重挂接到新的 baseline/session;冲突继续失败关闭。
65
+ - ✅ 同一发布同时包含 Form 设置和表单权限组时,必须使用一条 `resource publish form-setting,form-permission-group`,并以 `form-setting:<code>`、`form-permission-group:<code>` 精确限定 `--only`;SDD bundle 与 prepublish 校验共享该契约,禁止缺失、夹带、拆分发布或提前激活。
65
66
  - ✅ 未登记环境的旧工作区,正式 promotion 先聚合 mainline bundle 并 commit/push,再运行 `release publish --change <id> --profile <name>`。
66
67
  - ✅ 旧工作区已有 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。
67
68
  - ✅ 已通过 `environment init` 或 `environment attach` 接入的工作区使用 `release ship`,始终按 candidate → preproduction → production 执行。日常首条命令在预发停止,后续 `--confirm-production` 晋级;用户明确授权紧急发布时,首条命令可携带 `--confirm-production` 在一个命令内顺序完成两阶段,但不跳过预发、证据、CAS 或确认。candidate 封存源码、public、构建配置/脚本、稳定环境/资源绑定和两目标 Runtime 哈希产物;部署不现场重建,并将两条服务端 deployment 闭环为 `succeeded`。两套环境身份仍完全隔离,人工验收建议和 swap/policy/权限门禁保持不变。