openxiangda 1.0.243 → 1.0.244

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
@@ -131,6 +131,10 @@ instead of a database-column error.
131
131
 
132
132
  React SPA workspaces publish their frontend with `openxiangda runtime deploy`. Every finalized Runtime release is built from a clean, committed Git `HEAD`; the CLI freezes `sourceRevision`, the current active release, and its source revision before any build/upload. Deploy fails with `RUNTIME_SOURCE_BASE_DIVERGED` when its `HEAD` does not descend from the online Runtime source, including with `--no-activate`, so an old isolated worktree cannot stage and later activate a silent rollback. `.openxiangda/`, `openspec/`, `dist/`, and other pure generated/governance/state paths do not make the source dirty, but `--no-build` cannot bypass the lineage gate. An intentional rollback requires `--allow-runtime-rollback --reason "<at least 8 characters>"`, which is persisted for audit and never bypasses dirty/non-Git checks.
133
133
 
134
+ Environment-managed `release ship`, recovery `release deploy`, and `release promote` accept the same audited Runtime rollback pair. It is forwarded only to `runtime-stage`, never to resource stages or `app-finalize`. Ship freezes the intent in its private journal so production confirmation can inherit it automatically; without the explicit pair, lineage divergence remains blocked.
135
+
136
+ Before publishing the OpenXiangda npm package, run `npm run test:release` once on the final clean version commit. It writes hash-bound evidence under `.openxiangda/evidence/tests/<HEAD>/release.json`. `npm publish` validates that evidence against the exact Git commit, package version, Node version, and complete passing result, then runs the mainline guard; it does not execute the full release suite a second time. Any commit, version, runtime, result, or evidence-hash drift remains fail closed.
137
+
134
138
  Parallel tasks develop and test in isolated worktrees, but feature worktrees do not publish. After approved commits are merged and pushed, `sdd bundle <release-change> --changes ...` unions their exact structured scope and preserves the source changes' common Git baseline instead of adopting the post-merge `HEAD`. Old imported changes that predate source-base metadata must pass `--source-base-ref <commit>` explicitly. Commit/push the bundle, then run `release publish --change <release-change> --profile <name>` from a clean local main/master whose commit exactly equals the authoritative remote tip. It verifies without rewriting reviewed SDD files, waits for the app lease, freezes one authoritative App capture, executes exact Form/Backend/Runtime staged steps, atomically finalizes the Root App release, and records a resumable local execution journal. Runtime checks use the narrow head endpoint; immutable Git-base artifact hashes are reused across plans.
135
139
 
136
140
  Because promotion starts from an already-pushed authoritative mainline commit, `openxiangda release integration-status --change <change> --profile <name>` should pass immediately after activation. After a managed ship has ended, `--change` recovers lineage directly from the private `ship.json` or follows its production/preproduction deployment IDs to the matching `execution.json`; a recovery failure names the missing file or field. Run it and `release end`; there is no post-release merge step.
@@ -1012,6 +1012,14 @@ function releaseExecutionPath(changeId, deploymentId, cwd = process.cwd()) {
1012
1012
 
1013
1013
  function withManagedReleaseForwardedFlags(stepId, args = [], flags = {}) {
1014
1014
  const forwarded = [...args];
1015
+ if (stepId === 'runtime-stage' && flags['allow-runtime-rollback']) {
1016
+ forwarded.push('--allow-runtime-rollback');
1017
+ const rollbackReason = String(flags.reason || '').trim();
1018
+ if (rollbackReason) {
1019
+ forwarded.push('--reason', rollbackReason);
1020
+ }
1021
+ return forwarded;
1022
+ }
1015
1023
  const immutableResourceStages = new Set([
1016
1024
  'backend-stage',
1017
1025
  'workflow-stage',
package/lib/cli.js CHANGED
@@ -336,7 +336,7 @@ Usage:
336
336
  openxiangda retry <runId> [--environment preproduction|production] [--json]
337
337
  openxiangda rollback <preproduction|production> --to <appReleaseId> [--json]
338
338
  openxiangda release publish|begin|status|integration-status|renew|end [--change id] [--profile name] [--watch] [--json]
339
- openxiangda release ship|candidate|deploy|test|promote|rollback [--candidate id] [--environment target] [--replace-manifest --reason text] [--adopt-online-baseline --adoption-reason text] [--json]
339
+ openxiangda release ship|candidate|deploy|test|promote|rollback [--candidate id] [--environment target] [--replace-manifest] [--allow-runtime-rollback --reason text] [--adopt-online-baseline --adoption-reason text] [--json]
340
340
  openxiangda task status --change <id> [--profile name] [--watch] [--json]
341
341
  openxiangda release backend-head|backend-list|backend-detail|backend-diff|backend-rollback|backend-abort|backend-retry [releaseId] [--profile name] [--json]
342
342
  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]
@@ -1177,9 +1177,12 @@ function writeManagedShip(changeId, value) {
1177
1177
  }
1178
1178
 
1179
1179
  function normalizeManagedShipIntent(value = {}) {
1180
+ const enabled = Boolean(value.enabled);
1180
1181
  return {
1181
- enabled: Boolean(value.enabled),
1182
- reason: String(value.reason || '').trim() || null,
1182
+ enabled,
1183
+ reason: enabled
1184
+ ? String(value.reason || '').trim() || null
1185
+ : null,
1183
1186
  };
1184
1187
  }
1185
1188
 
@@ -1201,8 +1204,13 @@ function resolveManagedShipIntents(flags = {}, existingShip = null) {
1201
1204
  enabled: flags['adopt-online-baseline'],
1202
1205
  reason: flags['adoption-reason'],
1203
1206
  });
1207
+ const requestedRuntimeRollback = normalizeManagedShipIntent({
1208
+ enabled: flags['allow-runtime-rollback'],
1209
+ reason: flags.reason,
1210
+ });
1204
1211
  const hasFrozenCandidate = Boolean(existingShip?.candidateId);
1205
1212
  let baselineAdoption = requestedBaselineAdoption;
1213
+ let runtimeRollback = requestedRuntimeRollback;
1206
1214
 
1207
1215
  if (hasFrozenCandidate) {
1208
1216
  const recordedReplacement = normalizeManagedShipIntent(
@@ -1239,6 +1247,34 @@ function resolveManagedShipIntents(flags = {}, existingShip = null) {
1239
1247
  baselineAdoption = adoptionExplicitlySupplied
1240
1248
  ? requestedBaselineAdoption
1241
1249
  : recordedAdoption;
1250
+
1251
+ const hasRecordedRuntimeRollback = Object.prototype.hasOwnProperty.call(
1252
+ existingShip,
1253
+ 'runtimeRollback'
1254
+ );
1255
+ const recordedRuntimeRollback = normalizeManagedShipIntent(
1256
+ existingShip.runtimeRollback
1257
+ );
1258
+ const runtimeRollbackExplicitlySupplied =
1259
+ Object.prototype.hasOwnProperty.call(
1260
+ flags,
1261
+ 'allow-runtime-rollback'
1262
+ );
1263
+ if (
1264
+ hasRecordedRuntimeRollback &&
1265
+ runtimeRollbackExplicitlySupplied &&
1266
+ !managedShipIntentMatches(
1267
+ recordedRuntimeRollback,
1268
+ requestedRuntimeRollback
1269
+ )
1270
+ ) {
1271
+ fail(
1272
+ 'RELEASE_SHIP_RUNTIME_ROLLBACK_INTENT_MISMATCH: --allow-runtime-rollback/--reason 与该 candidate 的预发 Runtime 发布意图不一致;省略这两个参数可自动复用 ship.json 中的已冻结意图'
1273
+ );
1274
+ }
1275
+ runtimeRollback = runtimeRollbackExplicitlySupplied
1276
+ ? requestedRuntimeRollback
1277
+ : recordedRuntimeRollback;
1242
1278
  }
1243
1279
 
1244
1280
  const effectiveFlags = { ...flags };
@@ -1249,10 +1285,23 @@ function resolveManagedShipIntents(flags = {}, existingShip = null) {
1249
1285
  delete effectiveFlags['adopt-online-baseline'];
1250
1286
  delete effectiveFlags['adoption-reason'];
1251
1287
  }
1288
+ if (runtimeRollback.enabled) {
1289
+ const existingReason = String(effectiveFlags.reason || '').trim();
1290
+ if (existingReason && existingReason !== runtimeRollback.reason) {
1291
+ fail(
1292
+ 'RELEASE_SHIP_RUNTIME_ROLLBACK_REASON_CONFLICT: Runtime rollback 与 manifest replacement 共用 --reason 时必须使用同一审计原因'
1293
+ );
1294
+ }
1295
+ effectiveFlags['allow-runtime-rollback'] = true;
1296
+ effectiveFlags.reason = runtimeRollback.reason;
1297
+ } else {
1298
+ delete effectiveFlags['allow-runtime-rollback'];
1299
+ }
1252
1300
 
1253
1301
  return {
1254
1302
  manifestReplacement,
1255
1303
  baselineAdoption,
1304
+ runtimeRollback,
1256
1305
  effectiveFlags,
1257
1306
  };
1258
1307
  }
@@ -1271,12 +1320,17 @@ function buildManagedShipCommand(
1271
1320
  JSON.stringify(profileName),
1272
1321
  ];
1273
1322
  if (options.confirmProduction) args.push('--confirm-production');
1323
+ let sharedReason = null;
1274
1324
  if (intents.manifestReplacement.enabled) {
1275
- args.push(
1276
- '--replace-manifest',
1277
- '--reason',
1278
- JSON.stringify(intents.manifestReplacement.reason)
1279
- );
1325
+ args.push('--replace-manifest');
1326
+ sharedReason = intents.manifestReplacement.reason;
1327
+ }
1328
+ if (intents.runtimeRollback.enabled) {
1329
+ args.push('--allow-runtime-rollback');
1330
+ sharedReason = intents.runtimeRollback.reason;
1331
+ }
1332
+ if (sharedReason) {
1333
+ args.push('--reason', JSON.stringify(sharedReason));
1280
1334
  }
1281
1335
  if (intents.baselineAdoption.enabled) {
1282
1336
  args.push(
@@ -1972,6 +2026,7 @@ async function publishWorkspaceRelease(config, target, flags = {}) {
1972
2026
  if (!changeId) {
1973
2027
  fail('用法: openxiangda release publish --change <id> --profile <name>');
1974
2028
  }
2029
+ assertManagedRuntimeRollbackFlags(flags);
1975
2030
  const releaseCandidate = flags.__verifiedCandidate
1976
2031
  ? assertLocalCandidate(flags.__verifiedCandidate)
1977
2032
  : readReleaseCandidate(candidateId);
@@ -4390,6 +4445,7 @@ const RELEASE_SHIP_SUPPORTED_FLAGS = new Set([
4390
4445
  'since',
4391
4446
  'expand-dependencies',
4392
4447
  'replace-manifest',
4448
+ 'allow-runtime-rollback',
4393
4449
  'reason',
4394
4450
  'adopt-online-baseline',
4395
4451
  'adoption-reason',
@@ -4414,12 +4470,19 @@ function assertReleaseShipFlags(flags = {}) {
4414
4470
  throw error;
4415
4471
  }
4416
4472
  const replacementRequested = Boolean(flags['replace-manifest']);
4473
+ const runtimeRollbackRequested = Boolean(
4474
+ flags['allow-runtime-rollback']
4475
+ );
4417
4476
  const replacementReason = String(flags.reason || '').trim();
4418
- if (!replacementRequested && replacementReason) {
4477
+ if (
4478
+ !replacementRequested &&
4479
+ !runtimeRollbackRequested &&
4480
+ replacementReason
4481
+ ) {
4419
4482
  const error = new Error(
4420
- 'RELEASE_SHIP_REPLACE_MANIFEST_FLAG_REQUIRED: --reason 必须与 --replace-manifest 一起使用;命令已在任何 candidate/deployment 写入前终止'
4483
+ 'RELEASE_SHIP_SCOPED_REASON_FLAG_REQUIRED: --reason 必须与 --replace-manifest 或 --allow-runtime-rollback 一起使用;命令已在任何 candidate/deployment 写入前终止'
4421
4484
  );
4422
- error.code = 'RELEASE_SHIP_REPLACE_MANIFEST_FLAG_REQUIRED';
4485
+ error.code = 'RELEASE_SHIP_SCOPED_REASON_FLAG_REQUIRED';
4423
4486
  throw error;
4424
4487
  }
4425
4488
  if (replacementRequested && replacementReason.length < 8) {
@@ -4429,6 +4492,13 @@ function assertReleaseShipFlags(flags = {}) {
4429
4492
  error.code = 'RELEASE_SHIP_REPLACE_MANIFEST_REASON_REQUIRED';
4430
4493
  throw error;
4431
4494
  }
4495
+ if (runtimeRollbackRequested && replacementReason.length < 8) {
4496
+ const error = new Error(
4497
+ 'RELEASE_SHIP_RUNTIME_ROLLBACK_REASON_REQUIRED: --allow-runtime-rollback 必须提供至少 8 个字符的 --reason;命令已在任何 candidate/deployment 写入前终止'
4498
+ );
4499
+ error.code = 'RELEASE_SHIP_RUNTIME_ROLLBACK_REASON_REQUIRED';
4500
+ throw error;
4501
+ }
4432
4502
  const adoptionRequested = Boolean(flags['adopt-online-baseline']);
4433
4503
  const adoptionReason = String(flags['adoption-reason'] || '').trim();
4434
4504
  if (!adoptionRequested && adoptionReason) {
@@ -4447,6 +4517,13 @@ function assertReleaseShipFlags(flags = {}) {
4447
4517
  }
4448
4518
  }
4449
4519
 
4520
+ function assertManagedRuntimeRollbackFlags(flags = {}) {
4521
+ return resolveRuntimeRollbackBypass({
4522
+ allowRollback: flags['allow-runtime-rollback'],
4523
+ reason: flags.reason,
4524
+ });
4525
+ }
4526
+
4450
4527
  async function runApplicationShipCommand(
4451
4528
  config,
4452
4529
  initialTarget,
@@ -4458,7 +4535,7 @@ async function runApplicationShipCommand(
4458
4535
  readStringFlag(flags, 'change') || positional[0];
4459
4536
  if (!changeId) {
4460
4537
  fail(
4461
- '用法: openxiangda release ship --change <id> [--replace-manifest --reason "..."] [--adopt-online-baseline --adoption-reason "..."] [--confirm-production] [--acceptance-note "..."]'
4538
+ '用法: openxiangda release ship --change <id> [--replace-manifest] [--allow-runtime-rollback] [--reason "..."] [--adopt-online-baseline --adoption-reason "..."] [--confirm-production] [--acceptance-note "..."]'
4462
4539
  );
4463
4540
  }
4464
4541
  const logicalApp = requireLogicalApplication(initialTarget);
@@ -4478,6 +4555,7 @@ async function runApplicationShipCommand(
4478
4555
  const {
4479
4556
  manifestReplacement,
4480
4557
  baselineAdoption,
4558
+ runtimeRollback,
4481
4559
  effectiveFlags,
4482
4560
  } = intents;
4483
4561
 
@@ -4526,6 +4604,7 @@ async function runApplicationShipCommand(
4526
4604
  candidateFile: created.candidateFile,
4527
4605
  manifestReplacement,
4528
4606
  baselineAdoption,
4607
+ runtimeRollback,
4529
4608
  });
4530
4609
  } else {
4531
4610
  assertLocalCandidate(candidate);
@@ -4563,6 +4642,7 @@ async function runApplicationShipCommand(
4563
4642
  preproductionStatus: failure.deploymentStatus,
4564
4643
  manifestReplacement,
4565
4644
  baselineAdoption,
4645
+ runtimeRollback,
4566
4646
  failure,
4567
4647
  nextCommand: buildManagedShipCommand(
4568
4648
  changeId,
@@ -4585,6 +4665,7 @@ async function runApplicationShipCommand(
4585
4665
  preproductionStatus: deployment.status,
4586
4666
  manifestReplacement,
4587
4667
  baselineAdoption,
4668
+ runtimeRollback,
4588
4669
  failure: null,
4589
4670
  preparedAt:
4590
4671
  existingShip?.preparedAt ||
@@ -4752,6 +4833,7 @@ async function runApplicationShipCommand(
4752
4833
  productionStatus: failure.deploymentStatus,
4753
4834
  manifestReplacement,
4754
4835
  baselineAdoption,
4836
+ runtimeRollback,
4755
4837
  failure,
4756
4838
  productionPromotion: {
4757
4839
  status: 'failed',
@@ -4816,6 +4898,7 @@ async function runApplicationShipCommand(
4816
4898
  productionStatus: promoted.deployment.status,
4817
4899
  manifestReplacement,
4818
4900
  baselineAdoption,
4901
+ runtimeRollback,
4819
4902
  failure,
4820
4903
  productionPromotion: {
4821
4904
  status: 'evidence_failed',
@@ -4923,6 +5006,10 @@ async function runApplicationEnvironmentReleaseCommand(
4923
5006
  return { deployment: reconciled, reconciled: true };
4924
5007
  }
4925
5008
 
5009
+ if (['deploy', 'promote'].includes(subcommand)) {
5010
+ assertManagedRuntimeRollbackFlags(flags);
5011
+ }
5012
+
4926
5013
  const candidateId =
4927
5014
  readStringFlag(flags, 'candidate') ||
4928
5015
  readStringFlag(flags, 'candidate-id') ||
@@ -5044,13 +5131,14 @@ async function release(args) {
5044
5131
  if (wantsSubcommandHelp(subcommand, flags)) {
5045
5132
  print([
5046
5133
  '用法: openxiangda release publish|begin|status|explain|integration-status|renew|end [--change id] [--profile name] [--json]',
5047
- ' 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]',
5134
+ ' openxiangda release ship|candidate|deploy|reconcile|test|promote|rollback [--candidate id] [--environment target] [--replace-manifest] [--allow-runtime-rollback --reason text] [--adopt-online-baseline --adoption-reason text] [--confirm-production] [--json]',
5048
5135
  ' openxiangda release backend-head|backend-list|backend-detail|backend-diff|backend-rollback|backend-abort|backend-retry [releaseId] [--profile name] [--json]',
5049
5136
  ' 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]',
5050
5137
  '常用流程:',
5051
5138
  ' openxiangda release ship --change <id> --profile <name>',
5052
5139
  ' openxiangda release ship --change <id> --profile <name> --adopt-online-baseline --adoption-reason "已审计目标来自多次历史发布"',
5053
5140
  ' openxiangda release ship --change <id> --profile <name> --replace-manifest --reason "已审查精确 Function manifest"',
5141
+ ' openxiangda release ship --change <id> --profile <name> --allow-runtime-rollback --reason "已审计并批准 Runtime 源码基线回退"',
5054
5142
  ' # 确认预发结果后;可选 --acceptance-note 留下人工验收记录:',
5055
5143
  ' openxiangda release ship --change <id> --profile <name> --confirm-production',
5056
5144
  ' openxiangda release publish --change <id> --profile <name>',
@@ -5068,6 +5156,7 @@ async function release(args) {
5068
5156
  ' - candidate 封存环境/资源绑定和两目标 Runtime 哈希产物;预发与正式部署均不现场重建,并以 deployment succeeded 闭环。',
5069
5157
  ' - 已合入主线但线上来自多次历史 lineage 的精确非删除恢复可显式使用 --adopt-online-baseline 与 --adoption-reason;预发冻结后,正式确认会从 ship.json 自动复用同一意图,仍保留冻结 Head、CAS、租约、staged children 和单次 App finalize。',
5070
5158
  ' - ship 的 --replace-manifest 与 --reason 必须成对,只透传给精确 Backend Function/Automation stage;正式确认必须复用与预发完全相同的参数。',
5159
+ ' - --allow-runtime-rollback 与 --reason 必须成对,只透传给 runtime-stage;ship 会冻结并在正式确认自动复用该审计意图,app-finalize 不会收到这些参数。',
5071
5160
  ' - ship 当前不支持 --dry-run;任何未声明参数都会在 candidate/deployment 写入前 fail-closed。需要只读检查时使用 environment status/diff 与 release status。',
5072
5161
  ' - publish 默认等待租约并按私有执行日志恢复;上次写结果不确定时必须只读核对后显式 --resume-after-review。',
5073
5162
  ' - app-head 默认只输出紧凑 head 摘要;需要完整 manifest 时显式追加 --full。',
@@ -135,6 +135,8 @@ For an audited catch-up whose exact non-delete targets are already merged but wh
135
135
 
136
136
  When an environment-managed release intentionally replaces complete Function/Automation manifests, `release ship` may add the inseparable `--replace-manifest --reason "..."` pair. The reason must be at least 8 characters; only an exact Backend selector receives it, and the production confirmation must repeat the exact preproduction pair. It never widens Form, Workflow, Runtime, configuration, wildcard, or app-wide stages.
137
137
 
138
+ When the sealed Runtime source intentionally does not descend from the active Runtime lineage, managed `release ship`, recovery `release deploy`, and `release promote` may add `--allow-runtime-rollback --reason "..."`. The reason must be at least 8 characters. The CLI forwards the pair only to `runtime-stage`, never to Backend/Form/Workflow/configuration or `app-finalize`; ship freezes the pair in `ship.json`, and production confirmation automatically inherits it. Without the explicit pair, Runtime lineage remains fail closed.
139
+
138
140
  `resource plan` and publish dry-runs are strictly GET/HEAD-only. `READ_ONLY_AUTH_REQUIRED` means the access token expired; run `openxiangda auth refresh --profile <name>` or log in again before retrying. Never add an automatic refresh POST inside a plan.
139
141
 
140
142
  `release publish` is the default promotion entrypoint only for legacy unmanaged workspaces. It verifies without rewriting reviewed `change.json`/`release.json`, waits for the app lease, freezes the App capture after ownership is acquired, executes deterministic exact staged steps, resumes from `.openxiangda/releases/<change>/execution.json`, atomically finalizes, verifies mainline integration, and releases the lease. Environment-managed applications use the two-phase `release ship`; candidate/deploy/test/promote, `release begin`, and child commands remain recovery/diagnostic primitives.
@@ -138,6 +138,8 @@ When a reviewed catch-up contains exact non-delete targets that are already on a
138
138
 
139
139
  An environment-managed release may intentionally replace complete Function/Automation manifests with `release ship --replace-manifest --reason "..."`. The two flags are inseparable, the reason is at least 8 characters, only an exact Backend stage receives them, and production confirmation must repeat the exact preproduction intent. They never widen Form, Workflow, Runtime, configuration, wildcard, or app-wide stages.
140
140
 
141
+ For an audited Runtime source rollback, managed `release ship`, recovery `release deploy`, and `release promote` accept `--allow-runtime-rollback --reason "..."` with a reason of at least 8 characters. This pair is scoped only to `runtime-stage` and never reaches resource stages or `app-finalize`. Ship freezes it in `ship.json`; production confirmation inherits it automatically. The default remains fail closed.
142
+
141
143
  `release publish` is the normal whole-app entrypoint for legacy unmanaged workspaces: it verifies SDD without mutating reviewed files, waits for the promotion lease, freezes one App capture, stages the exact Form/Backend/Runtime children, resumes from a private execution journal, finalizes once, and releases the lease. Managed applications use two-phase `release ship` and its deployment-scoped journal. Individual candidate/deploy/test/promote commands are recovery/diagnostic primitives.
142
144
 
143
145
  Reviewed bundle commands may retain `<profile>` as a template. The explicit real `release publish --profile <name>` value is bound to actual child argv without rewriting tracked SDD. React SPA page codes remain logical coverage targets and activate through the single Runtime child; they do not require PageRelease. If local lease state disappears, `release end --change <id>` reconciles a self-owned remote lease from the private execution journal and never reports inactive while a remote lease is active.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.243",
3
+ "version": "1.0.244",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -80,7 +80,7 @@
80
80
  "test:dependency-capsule": "node scripts/dependency-capsule-smoke.mjs",
81
81
  "test:typed-resource-plan": "node scripts/typed-resource-plan-smoke.mjs",
82
82
  "test:integration-bundle": "node scripts/integration-bundle-smoke.mjs",
83
- "prepublishOnly": "npm run test:critical && node scripts/release-mainline-guard.mjs",
83
+ "prepublishOnly": "npm run release:evidence:verify && node scripts/release-mainline-guard.mjs",
84
84
  "prepack": "npm run source:admin-list && npm run build:sdk",
85
85
  "test:profile-isolation": "bash scripts/profile-isolation-smoke.sh",
86
86
  "test:resource-plan": "node scripts/resource-plan-smoke.mjs",
@@ -108,6 +108,8 @@
108
108
  "test:release-telemetry": "node scripts/release-telemetry-smoke.mjs",
109
109
  "test:release-explain": "node scripts/release-explain-smoke.mjs",
110
110
  "test:release-error-classification": "node scripts/release-error-classification-smoke.mjs",
111
+ "test:release-evidence": "node scripts/release-evidence-guard-smoke.mjs",
112
+ "release:evidence:verify": "node scripts/release-evidence-guard.mjs",
111
113
  "test:delivery-v2-package": "node scripts/delivery-v2-package-smoke.mjs",
112
114
  "test:delivery-v2-executor": "node scripts/delivery-v2-executor-smoke.mjs",
113
115
  "test:task-status": "node scripts/task-status-smoke.mjs",
@@ -66,6 +66,8 @@ openxiangda commands --json
66
66
 
67
67
  环境托管发布需要完整替换 Function/Automation manifest 时,`release ship` 可成对增加 `--replace-manifest --reason "..."`;reason 至少 8 字符,只会进入精确 Backend selector,正式确认必须复用与预发完全相同的参数,绝不扩散到表单、流程、Runtime、配置资源或全量范围。
68
68
 
69
+ 环境托管发布确需审计式 Runtime 源码血缘回退时,`release ship`、恢复命令 `release deploy` 和 `release promote` 可成对增加 `--allow-runtime-rollback --reason "..."`;reason 至少 8 字符,只进入 `runtime-stage`,不进入资源阶段或 `app-finalize`。ship 会冻结该意图,正式确认自动复用;省略时继续失败关闭。
70
+
69
71
  受支持的配置资源(如 `publicAccessPolicies`)以精确 `resourceSelectors` 为发布边界;历史 `resources=true` 类别标记会被精确 selector 收窄。缺失 selector、未知类型、通配符 `*`、删除和真正全量资源仍必须 fail closed。
70
72
 
71
73
  `resource plan` 与 publish dry-run 严格只允许 GET/HEAD。遇到 `READ_ONLY_AUTH_REQUIRED` 时,先执行 `openxiangda auth refresh --profile <name>` 或重新登录再重试;不得在 plan 内自动 POST 刷新 token。
@@ -68,6 +68,7 @@ Delivery V2 自动从期望状态计算精确资源范围,封存内容寻址 A
68
68
  - ✅ 托管发布完成后运行 `release integration-status --change <change> --profile <name> --check`。CLI 会从私有 `ship.json` 恢复血缘,必要时自动沿 production/preproduction deployment ID 查找对应 `execution.json`;失败信息必须指出实际缺失的日志或字段。
69
69
  - ✅ 只有已审计目标早已进入权威主线、线上却由多次历史 lineage 组成且无法对应单一 Git 基线时,第一次 `release ship` 才可增加 `--adopt-online-baseline --adoption-reason "..."`;该意图冻结进私有 `ship.json` 并由后续 `--confirm-production` 自动复用。仅允许精确非删除 selectors,冻结 Head、change/lease、服务端 CAS、staged children 与单次 App finalize 仍是硬门禁。
70
70
  - ✅ 环境托管发布需要完整替换 Function/Automation manifest 时,`release ship` 可成对增加 `--replace-manifest --reason "..."`;reason 至少 8 字符,只透传精确 Backend selector,正式确认必须复用同一对参数,不扩散到 Form/Workflow/Runtime/配置或全量范围。
71
+ - ✅ 环境托管发布确需审计式 Runtime 源码血缘回退时,`release ship`、恢复命令 `release deploy` 和 `release promote` 可成对增加 `--allow-runtime-rollback --reason "..."`;reason 至少 8 字符,只透传 `runtime-stage`,不进入资源阶段或 `app-finalize`。ship 冻结该意图并由正式确认自动复用;默认仍失败关闭。
71
72
  - ✅ 受支持的配置资源(如 `publicAccessPolicies`)以精确 `resourceSelectors` 为边界;历史 `resources=true` 类别标记会被精确 selector 收窄。缺失 selector、未知类型、通配符 `*`、删除和真正全量资源仍 fail closed。
72
73
  - ✅ 本地开发者可运行 `openxiangda studio` 查看两套环境、差异、候选、部署和测试证据;该页面只监听回环地址且只暴露注册动作,生产操作仍需显式确认。
73
74