openxiangda 1.0.253 → 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 +4 -0
- package/lib/application-environments.js +26 -12
- package/lib/sdd.js +38 -16
- package/openxiangda-skills/SKILL.md +3 -1
- package/openxiangda-skills/references/workspace-state.md +1 -0
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +4 -0
- package/package.json +1 -1
- package/templates/openxiangda-react-spa/.cursor/rules/openxiangda.mdc +1 -0
- package/templates/openxiangda-react-spa/.qoder/rules/openxiangda.md +1 -0
- package/templates/openxiangda-react-spa/AGENTS.md +4 -0
- package/templates/sy-lowcode-app-workspace/.cursor/rules/openxiangda.mdc +1 -0
- package/templates/sy-lowcode-app-workspace/.qoder/rules/openxiangda.md +1 -0
- package/templates/sy-lowcode-app-workspace/AGENTS.md +2 -0
package/README.md
CHANGED
|
@@ -128,8 +128,12 @@ User tokens are stored in `~/.openxiangda/profiles.json` with `0600` permissions
|
|
|
128
128
|
|
|
129
129
|
An environment-managed workspace keeps one logical application with independent `preproduction` and `production` targets. Each target owns its own `appType`, resource IDs, release heads, data, and side-effect policy; IDs must never be copied across targets. `release ship` always executes the same ordered candidate → preproduction → production protocol. The normal first invocation seals the candidate, deploys only to preproduction, and stops at `awaiting_production_confirmation`; a later invocation with `--confirm-production` promotes it. For an explicitly authorized emergency, supplying `--confirm-production` on the first invocation runs both phases in one command without bypassing preproduction, CAS, evidence, or production confirmation. Candidate sealing covers source, `public/`, build controls/scripts, stable environment/resource bindings, and target-specific hashed Runtime artifacts. Both deployments upload those artifacts with `--no-build`; each deployment is completed with evidence and reaches terminal `succeeded`, so it cannot leave the target slot blocked. Unrelated commits may land on authoritative mainline between phases only while the sealed commit remains an ancestor and every sealed input/binding/artifact still validates. Real human acceptance remains the recommended default and `--acceptance-note` records it. For audited historical-lineage adoption or reviewed Backend manifest replacement, the existing paired flags and exact-scope gates remain mandatory. Supported configuration resources use exact `resourceSelectors`; unknown, wildcard, destructive, and genuinely unscoped generic resources remain blocked. Lower-level candidate/deploy/test/fail/promote commands are recovery primitives. `release fail` requires an explicit preproduction target, deployment ID, and audit message; it verifies the deployment belongs to that preproduction environment before writing optional code/details to the platform failure audit. Direct `release publish` is retained only for legacy unmanaged workspaces.
|
|
130
130
|
|
|
131
|
+
DataView `status` is a last-observed lifecycle value, not a stable candidate binding: the same managed deployment legitimately moves it between `draft` and `active`. Candidate creation and validation therefore omit only `resources.dataViews.<code>.status`, including when resuming a candidate sealed by an older CLI. `dataViewId`, `materializedViewName`, `storageMode`, candidate hashes, environment identity, CAS, lease, source and every non-DataView status remain fail-closed.
|
|
132
|
+
|
|
131
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.
|
|
132
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
|
+
|
|
133
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.
|
|
134
138
|
|
|
135
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.
|
|
@@ -33,6 +33,15 @@ const CANDIDATE_RESOURCE_OBSERVATION_FIELDS = new Set([
|
|
|
33
33
|
'bundlePublishedAt',
|
|
34
34
|
]);
|
|
35
35
|
|
|
36
|
+
function isCandidateResourceObservationField(pathParts, key) {
|
|
37
|
+
if (CANDIDATE_RESOURCE_OBSERVATION_FIELDS.has(key)) return true;
|
|
38
|
+
return (
|
|
39
|
+
key === 'status' &&
|
|
40
|
+
pathParts.length === 2 &&
|
|
41
|
+
pathParts[0] === 'dataViews'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
function canonicalJson(value) {
|
|
37
46
|
if (Array.isArray(value)) {
|
|
38
47
|
return `[${value.map(item => canonicalJson(item)).join(',')}]`;
|
|
@@ -707,18 +716,22 @@ function normalizeCandidateProjectStateForHash(input, hashPolicy) {
|
|
|
707
716
|
return state;
|
|
708
717
|
}
|
|
709
718
|
|
|
710
|
-
function normalizeCandidateResourceBindings(value) {
|
|
719
|
+
function normalizeCandidateResourceBindings(value, pathParts = []) {
|
|
711
720
|
if (Array.isArray(value)) {
|
|
712
|
-
return value.map(item =>
|
|
721
|
+
return value.map((item, index) =>
|
|
722
|
+
normalizeCandidateResourceBindings(item, [...pathParts, String(index)])
|
|
723
|
+
);
|
|
713
724
|
}
|
|
714
725
|
if (!value || typeof value !== 'object') return value;
|
|
715
726
|
return Object.fromEntries(
|
|
716
727
|
Object.entries(value)
|
|
717
|
-
.filter(
|
|
728
|
+
.filter(
|
|
729
|
+
([key]) => !isCandidateResourceObservationField(pathParts, key)
|
|
730
|
+
)
|
|
718
731
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
719
732
|
.map(([key, item]) => [
|
|
720
733
|
key,
|
|
721
|
-
normalizeCandidateResourceBindings(item),
|
|
734
|
+
normalizeCandidateResourceBindings(item, [...pathParts, key]),
|
|
722
735
|
])
|
|
723
736
|
);
|
|
724
737
|
}
|
|
@@ -819,18 +832,19 @@ function inspectCandidateEnvironmentBindings(candidate, state) {
|
|
|
819
832
|
});
|
|
820
833
|
}
|
|
821
834
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
)
|
|
835
|
+
const expectedResources = normalizeCandidateResourceBindings(
|
|
836
|
+
expectedBinding?.resources || {}
|
|
837
|
+
);
|
|
838
|
+
const currentResources = normalizeCandidateResourceBindings(
|
|
839
|
+
currentBinding.resources || {}
|
|
840
|
+
);
|
|
841
|
+
if (!candidateBindingValueContains(expectedResources, currentResources)) {
|
|
828
842
|
mismatches.push({
|
|
829
843
|
targetName,
|
|
830
844
|
field: 'resources',
|
|
831
845
|
reason: 'sealed-resource-binding-changed',
|
|
832
|
-
expected:
|
|
833
|
-
current:
|
|
846
|
+
expected: expectedResources,
|
|
847
|
+
current: currentResources,
|
|
834
848
|
});
|
|
835
849
|
}
|
|
836
850
|
}
|
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
|
-
|
|
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.
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
(
|
|
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
|
|
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 (
|
|
2753
|
+
if (expectedFormTypes.length > 0) {
|
|
2733
2754
|
const formStage = normalized.find(
|
|
2734
2755
|
command =>
|
|
2735
|
-
/^openxiangda\s+resource\s+publish\
|
|
2756
|
+
/^openxiangda\s+resource\s+publish\b/.test(command) &&
|
|
2736
2757
|
!/\s--activate(?:\s|$)/.test(command) &&
|
|
2737
2758
|
sameStringSet(
|
|
2738
|
-
|
|
2739
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
|
@@ -141,6 +141,8 @@ openxiangda release ship --change <release-change> --profile <name> \
|
|
|
141
141
|
|
|
142
142
|
For a workspace registered by `environment init` or connected by `environment attach`, production is never a direct publish target. `release ship` always executes candidate → preproduction → production. The normal first invocation stops after preproduction; a later `--confirm-production` promotes the same candidate. If the user explicitly authorizes an emergency, putting `--confirm-production` on the first invocation runs both phases in one command without skipping preproduction, evidence, CAS, or confirmation. The sealed candidate contains source/build/public/script inputs, stable environment/resource bindings, and hashed target-specific Runtime artifacts; deployment uploads those exact artifacts with no rebuild and closes each server deployment as `succeeded`. Human acceptance remains recommended and may be recorded with `--acceptance-note`. Lower-level candidate/deploy/test/fail/promote commands are recovery primitives. `release fail` is preproduction-only: pass an explicit deployment and audit message, and the CLI verifies the deployment belongs to the selected preproduction environment before it writes optional code/details. Keep preproduction and production identities isolated, and retain the existing authorization/CAS rules for environment swap and policy changes. Use `openxiangda studio` for bindings, drift, evidence, and safe next actions.
|
|
143
143
|
|
|
144
|
+
Candidate binding checks treat only `resources.dataViews.<code>.status` as a mutable platform lifecycle observation, so a candidate survives its own `active → draft → active` staging sequence and older sealed candidates remain resumable. DataView IDs, materialized-view names, storage modes, other resource statuses, candidate hashes, environment identity, CAS, lease, and source/mainline checks remain exact.
|
|
145
|
+
|
|
144
146
|
A sealed candidate may be promoted from a later clean, pushed authoritative mainline commit only when the candidate commit remains its Git ancestor and every sealed candidate input file still has the exact recorded hash. This permits unrelated parallel merges without allowing stale candidate inputs to overwrite newer work. Do not edit private candidate metadata to bypass `CANDIDATE_INPUTS_CHANGED`. The CLI waits for both the app lease and target deployment slot; each target permits only one running or evidence-pending deployment. Emergency fixes stay on the same candidate → preproduction → production path with a narrow L1 scope. `--wait-seconds 0` is fail-fast, not a binding-contract, CAS, or production-confirmation bypass.
|
|
145
147
|
|
|
146
148
|
For an audited catch-up whose exact non-delete targets are already merged but whose active resources combine multiple historical release lineages, the first ship invocation may add `--adopt-online-baseline --adoption-reason "..."`. Ship validates and freezes the pair before candidate/deployment creation; the later `--confirm-production` invocation automatically reuses the same intent from the private ship journal and forwards it only to exact scoped resource stages. Frozen online heads, change/lease ownership, delete/prune/force rejection, server CAS, staged-child verification, and the single atomic App finalize remain mandatory.
|
|
@@ -102,6 +102,7 @@ Environment-managed workspaces add a logical application and target-specific bin
|
|
|
102
102
|
- Local resource keys are logical codes.
|
|
103
103
|
- Live IDs and lightweight runtime aliases are nested under the profile that produced them.
|
|
104
104
|
- `resources.dataViews` is keyed by data view `code` and stores only profile-local platform metadata such as `dataViewId`, `materializedViewName`, and last known `status`.
|
|
105
|
+
- The stored DataView `status` is observational: managed staging legitimately moves it between `draft` and `active`. Candidate binding validation ignores only that field, including for older sealed candidates, while keeping `dataViewId`, `materializedViewName`, `storageMode`, and all non-DataView status fields strict.
|
|
105
106
|
- Data view definitions, refresh config, permissions, and source `formCode` references belong in `src/resources/data-views/*.json`, not in state.
|
|
106
107
|
- Do not store business configuration or secrets in `.openxiangda/state.json`; store those in `src/resources/`.
|
|
107
108
|
- CLI writes use a lock, a three-way merge at profile/resource-key granularity, and fsync + atomic rename. Concurrent writes to different profiles or logical resource keys are preserved; competing writes to the same field fail with `OPENXIANGDA_STATE_CONFLICT` and must be retried from a freshly loaded state.
|
|
@@ -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
|
|
@@ -144,6 +146,8 @@ openxiangda release ship --change <release-change> --profile <name> \
|
|
|
144
146
|
|
|
145
147
|
Once `environment init` registers a logical application, or `environment attach` connects an existing workspace, `preproduction` and `production` are separate target bindings with separate app/resource/data IDs. Never copy IDs between them. Existing legacy resource mappings may seed only the appType-matching target. `release ship` always runs candidate → preproduction → production. Normally the first invocation stops at `awaiting_production_confirmation` and a later `--confirm-production` invocation promotes the same candidate. When the user explicitly authorizes an emergency release, `--confirm-production` may be supplied on the first invocation to execute both phases in one command; it bypasses no preproduction, evidence, CAS, or confirmation gate. Candidate sealing includes source/build/public/script inputs, stable environment/resource bindings, and hashed target-specific Runtime artifacts. Deployment reuses those artifacts without rebuilding and completes both server deployment records to `succeeded`. Human acceptance remains recommended and an optional `--acceptance-note` records it. Direct `release publish` to either managed target fails closed. Environment swap and policy updates retain their existing explicit authorization, CAS, and permission rules. `openxiangda studio` is the local loopback-only developer view and exposes only registered safe actions.
|
|
146
148
|
|
|
149
|
+
Treat DataView `status` as a platform lifecycle observation during candidate binding checks. The same deployment may change `resources.dataViews.<code>.status` between `draft` and `active`, including between preproduction and production confirmation, without invalidating the sealed candidate. Continue to validate the DataView ID, materialized-view name, storage mode, every other resource status, hashes, environment identity, CAS, lease, and source/mainline evidence exactly.
|
|
150
|
+
|
|
147
151
|
A sealed candidate may continue from a later clean, pushed authoritative mainline commit only when its commit remains a Git ancestor and all sealed release inputs retain their exact hashes. If any input changed, create a new candidate; never edit the private candidate file. The CLI waits for the app lease and target deployment slot before writes, and the platform permits one running or evidence-pending deployment per target. Emergency releases still use a narrow L1 scope and the same candidate → preproduction → production path; `--wait-seconds 0` only fails fast.
|
|
148
152
|
|
|
149
153
|
When a reviewed catch-up contains exact non-delete targets that are already on authoritative mainline but the active application combines several historical release lineages, the first ship invocation may add `--adopt-online-baseline --adoption-reason "..."`. Ship validates and freezes the pair before candidate/deployment creation. The later `--confirm-production` invocation automatically reuses the same intent from `ship.json` and forwards it only to exact scoped resource stages. It does not relax frozen online heads, change/lease ownership, delete/prune/force rejection, server CAS, staged-child verification, or the single atomic App finalize.
|
package/package.json
CHANGED
|
@@ -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,8 +58,12 @@ 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
|
|
|
65
|
+
DataView 的 `status` 是平台生命周期观察值;同一托管部署在预发暂存和正式确认之间发生 `active → draft → active` 不构成候选漂移。候选仍严格校验 `dataViewId`、`materializedViewName`、`storageMode`、其他资源状态、哈希、环境身份、CAS、租约和来源主线。
|
|
66
|
+
|
|
63
67
|
预发 UAT 未通过时,使用 `release fail --deployment <id> --message "..." [--code <code>] [--details-json <JSON|file>] --environment preproduction --profile <name>` 留下平台审计记录。CLI 会先核对 deployment 属于所选预发环境;禁止对 production target 或不匹配的 deployment 使用。
|
|
64
68
|
|
|
65
69
|
托管发布完成后运行 `release integration-status --change <change> --profile <name> --check`。CLI 会从私有 `ship.json` 恢复血缘,必要时自动沿 production/preproduction deployment ID 查找对应 `execution.json`;失败信息必须指出实际缺失的日志或字段。
|
|
@@ -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,9 +62,11 @@ 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/权限门禁保持不变。
|
|
69
|
+
- ✅ DataView `status` 仅是平台生命周期观察值;同一托管部署导致的 `active → draft → active` 不应让候选失效。`dataViewId`、`materializedViewName`、`storageMode`、其他资源状态、哈希、环境身份、CAS、租约和来源主线仍严格校验。
|
|
68
70
|
- ✅ 预发 UAT 未通过时,使用 `release fail --deployment <id> --message "..." [--code <code>] [--details-json <JSON|file>] --environment preproduction --profile <name>` 写入平台审计。CLI 会先核对 deployment 属于所选预发环境;production target 或不匹配的 deployment 必须在写入前拒绝。
|
|
69
71
|
- ✅ 托管发布完成后运行 `release integration-status --change <change> --profile <name> --check`。CLI 会从私有 `ship.json` 恢复血缘,必要时自动沿 production/preproduction deployment ID 查找对应 `execution.json`;失败信息必须指出实际缺失的日志或字段。
|
|
70
72
|
- ✅ 只有已审计目标早已进入权威主线、线上却由多次历史 lineage 组成且无法对应单一 Git 基线时,第一次 `release ship` 才可增加 `--adopt-online-baseline --adoption-reason "..."`;该意图冻结进私有 `ship.json` 并由后续 `--confirm-production` 自动复用。仅允许精确非删除 selectors,冻结 Head、change/lease、服务端 CAS、staged children 与单次 App finalize 仍是硬门禁。
|