openxiangda-skill-kit 2.0.0-alpha.99 → 2.0.1

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.
Files changed (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -7
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +9 -19
  6. package/dist/index.js.map +1 -1
  7. package/dist/workspace-guidance.d.ts +13 -0
  8. package/dist/workspace-guidance.d.ts.map +1 -0
  9. package/dist/workspace-guidance.js +67 -0
  10. package/dist/workspace-guidance.js.map +1 -0
  11. package/package.json +12 -3
  12. package/skills/manifest.json +2 -2
  13. package/skills/openxiangda-v2/SKILL.md +63 -46
  14. package/skills/openxiangda-v2/agents/openai.yaml +2 -2
  15. package/skills/openxiangda-v2/references/administration.md +27 -0
  16. package/skills/openxiangda-v2/references/application-foundation.md +162 -0
  17. package/skills/openxiangda-v2/references/appspec.md +132 -47
  18. package/skills/openxiangda-v2/references/backend.md +101 -237
  19. package/skills/openxiangda-v2/references/cli.md +27 -0
  20. package/skills/openxiangda-v2/references/concepts.md +61 -0
  21. package/skills/openxiangda-v2/references/data-authz.md +36 -244
  22. package/skills/openxiangda-v2/references/delivery.md +112 -42
  23. package/skills/openxiangda-v2/references/development.md +32 -0
  24. package/skills/openxiangda-v2/references/field-components.md +236 -0
  25. package/skills/openxiangda-v2/references/frontend.md +269 -101
  26. package/skills/openxiangda-v2/references/getting-started.md +66 -0
  27. package/skills/openxiangda-v2/references/interaction-patterns.md +56 -0
  28. package/skills/openxiangda-v2/references/mcp.md +649 -0
  29. package/skills/openxiangda-v2/references/product-design.md +142 -0
  30. package/skills/openxiangda-v2/references/public-access.md +167 -0
  31. package/skills/openxiangda-v2/references/testing.md +45 -48
  32. package/skills/openxiangda-v2/references/upgrading.md +39 -0
  33. package/skills/openxiangda-v2/references/workflow-events.md +152 -151
  34. package/skills/openxiangda-v2/references/architecture.md +0 -7
  35. package/skills/openxiangda-v2/references/commands.md +0 -21
  36. package/skills/openxiangda-v2/references/discovery.md +0 -15
  37. package/skills/openxiangda-v2/references/workspace.md +0 -25
@@ -1,146 +1,50 @@
1
- # OpenXiangda 2.0 Data and Authorization
1
+ # Data API 与权限
2
2
 
3
- Declare a resource and every field exactly once in `openxiangda.config.ts`.
4
- Do not write `schemaVersion`, `appCode`, `schema`, `capabilities`, `surface` or
5
- resource-level `fieldPolicies`; those are compiler projections, not application
6
- source. Do not create `platform/data` modules.
3
+ 授权由四层组成:页面/操作 capability、行谓词、字段 read,以及字段 create/update。前端只消费平台返回的最终访问结果来隐藏按钮、只读输入和剔除 payload;平台每次请求重新执行权威校验。
7
4
 
8
- ```ts
9
- import {
10
- currentUserDataPolicy,
11
- dataPolicyExpression,
12
- defineOpenXiangdaApp,
13
- resourceCapabilityCodes,
14
- resourceReadPolicy,
15
- } from 'openxiangda/config';
16
-
17
- const APP_CODE = 'visitor-center';
18
- const reservations = resourceCapabilityCodes(APP_CODE, 'visitor-reservations');
5
+ 以下仅以仪器管理应用举例,不是平台默认模型或角色。该示例的行规则是:学校管理员不受行谓词限制;学院管理员按记录 `collegeId` 匹配;仪器管理员按 `instrumentAdminIds` 包含当前用户匹配。不要增加影子范围字段。
19
6
 
20
- const visitorReservations = {
21
- code: 'visitor-reservations',
22
- name: '访客预约',
23
- dataPolicyCode: 'reservation-host',
24
- fields: [
25
- {
26
- code: 'visitorName', type: 'text.short', label: '访客姓名', required: true,
27
- indexed: true, list: true, filter: true, searchable: true, sortable: true,
28
- section: '访客信息',
29
- },
30
- {
31
- code: 'visitDate', type: 'date', label: '来访日期', required: true,
32
- list: true, filter: true, sortable: true, section: '来访安排',
33
- },
34
- {
35
- code: 'hostUserId', type: 'user.single', label: '接待人', required: true,
36
- list: true, filter: true,
37
- },
38
- {
39
- code: 'hostDepartmentId', type: 'department.single', label: '接待部门',
40
- list: true, filter: true,
41
- },
42
- {
43
- code: 'attachments', type: 'file', label: '附件',
44
- file: { maxCount: 5, maxSizeMb: 20, accept: ['image/*', '.pdf'] },
45
- },
46
- {
47
- code: 'internalNote', type: 'text.long', label: '内部备注',
48
- access: { read: ['app:visitor-center:internal-note:read'], update: false },
49
- },
50
- ],
51
- list: { defaultPageSize: 20, defaultSort: { field: 'visitDate', order: 'desc' } },
52
- form: { layout: 'sections' }, detail: { layout: 'sections' },
53
- mobile: { enabled: true },
54
- };
55
- ```
7
+ `collegeId` 是应用 `colleges` Native Resource 的记录 UUID。学院 scope dimension 通过
8
+ `valueSource.kind=native_resource` 绑定同一资源,选择器只展示平台按当前 membership 与
9
+ create/update 闭包返回的值。人员和部门保存平台目录真实 ID;部门不等于学院,也不能作为
10
+ 学院范围的隐式来源。
56
11
 
57
- Put it in `data: { resources: [visitorReservations] }`. Resource CRUD
58
- capabilities are derived by `resourceCapabilityCodes`; grant the needed values
59
- to roles. Directory-backed roles also require
60
- `app:<app-code>:directory:read`. Explicit field `access` capability codes are
61
- also granted only to the intended roles. They are owned and exported by the
62
- generated field policy, so do not repeat them in `authz.capabilities`.
63
- `authz.capabilities` contains only explicit `backend` or `ui` capabilities.
12
+ 字段策略支持 `read`、`create`、`update` `mask`。显式空数组拒绝,能力数组采用 all-of。无权更新字段不仅 disabled,还必须从更新 payload 删除。
64
13
 
65
- Declare mutation ownership on the same resource with
66
- `mutationOwner: 'native' | 'action' | 'readonly' | 'workflow'`. Native defaults
67
- to generated list/detail/create/update/delete. Other owners default to readable
68
- list/detail only and must use their named action or Workflow boundary for
69
- mutation. Use `generated: { list, detail, create, update, delete }` to narrow
70
- the generated page/operation surface. The compiler rejects Native mutation
71
- pages, AI operations or role grants on a non-Native owner, and rejects
72
- create/update when no writable business field exists. Do not grant a resource
73
- create/update/delete capability to make an action-owned record editable.
74
-
75
- Declare optional work Perspectives once at the application root. A Perspective
76
- references existing role codes; the compiler derives its readable capability
77
- projection, so never hand-author `capabilityCodes` or duplicate row filters:
78
-
79
- ```ts
80
- export default defineOpenXiangdaApp({
81
- // ...identity, authz and data...
82
- perspectives: [
83
- {
84
- code: 'reception-desk',
85
- name: '接待人员视角',
86
- roleCodes: ['reception_staff'],
87
- default: true,
88
- },
89
- {
90
- code: 'visitor-admin',
91
- name: '访客管理员视角',
92
- roleCodes: ['visitor_admin'],
93
- },
94
- ],
95
- });
14
+ ```bash
15
+ pnpm openxiangda check
96
16
  ```
97
17
 
98
- The runtime offers only Perspectives whose `roleCodes` intersect the current
99
- user's actual application-role union. Selecting one sends
100
- `X-OpenXiangda-Perspective` on standard reads. Native Data intersects the
101
- normal authorized role union with that Perspective before capability, field,
102
- row-policy and RLS evaluation. Omit the header for the complete union. Never
103
- use Perspective to guard writes, workflows or custom actions.
18
+ 角色成员、维度授权和平台管理员由平台管理面维护,不属于应用开发 CLI。
104
19
 
105
- `required: true` owns both storage nullability and form validation. A field
106
- without `access` inherits the resource read/create/update capability. Each
107
- access array is all-of; `false` is explicit deny. The same arrays drive the
108
- generated UI and platform field policies.
20
+ 自定义 PC/移动页面需要维护当前应用角色时,使用
21
+ `openxiangda/core` `loadRoleManagementCatalog`、
22
+ `listRoleMemberships`、`searchRoleManagementUsers`、成员 mutation
23
+ role-management-grant mutation。应用/平台超级管理员可以把全部角色或明确的
24
+ 目标角色集合委托给一个业务角色;普通业务管理者只有同时具备
25
+ `management.delegate` 时,才能把自己已有的目标角色和动作子集继续委托。
26
+ 平台按当前用户角色并集重算,接口不接受 actor、tenant、active role 或
27
+ impersonation token。mutation 必须携带 UUID `operationId`、`reason`,更新/撤销还必须
28
+ 携带最新 `expectedRevision`;409 后重新加载,不能猜 revision。使用前读取当前角色管理目录,详见[管理入口](administration.md)。
109
29
 
110
- For `uuid` fields, omission never generates a value. Only the resource system
111
- field `id` is platform-generated. An omitted optional UUID remains `null`; a
112
- required UUID must be supplied explicitly and cannot rely on an implicit
113
- database default.
30
+ 匿名外部访问不属于 RBAC 角色或 current-user 行策略。公开表单、续填、附件、重复校验和同一
31
+ 浏览器的本人记录访问只通过[`frontend.publicAccess` 专用合同](public-access.md)开放;平台继续在
32
+ 专用端点和 PostgreSQL/RLS 中强制匿名主体、字段、策略及提交回执边界。
114
33
 
115
- Field types are semantic, not PostgreSQL storage aliases. Use the catalog in the generated `AGENTS.md`: for example `text.short`, `number.integer`, `user.single`, `department.multiple`, `resource-ref.single`, `file`, `address` and `subtable`. The compiler alone chooses storage columns and constraints. Reference fields store JSON display values. For `resource-ref.*`, `resourceCode`, `value`, `label`, optional `description` and optional `snapshot` are convenient historical display data only: the target resource remains authoritative, the platform does not create a foreign key or refresh/check the stored JSON, and business actions that need current target state must query it by `resourceCode` plus `value`. A resource source `labelField` must point to `text.short` or `text.long`; a `serial-number` field can be listed in `searchFields`, `descriptionFields` or `snapshotFields`, but it is not a display label. File limits exist only under `file`; `maxCount` owns the single/multiple bound and `maxSizeMb` owns the per-file size bound. There is no `file.multiple` key.
116
-
117
- Generated list, detail, audit and preview surfaces render the stored canonical
118
- `label` snapshots for `option.*`, `user.*`, `department.*` and
119
- `resource-ref.*`, including multiple arrays and the first linked list column.
120
- Do not add application formatters, directory re-queries or browser-side joins
121
- for these standard fields.
122
-
123
- Numeric bounds belong on the field declaration and are enforced by the
124
- platform for every write path. They are inclusive, and only valid on
125
- `number.integer` or `number.decimal`:
126
-
127
- ```ts
128
- { code: 'capacity', type: 'number.integer', label: '容量', required: true, min: 0, max: 10000 }
129
- ```
130
-
131
- Cross-field rules belong on the resource, not in a generated form callback.
132
- Only bounded same-record field comparisons are supported; declare multiple
133
- invariants when all must hold:
34
+ 数值边界直接声明在字段上,`min`/`max` 为闭区间,并且只允许用于
35
+ `number.integer` 和 `number.decimal`。跨字段约束声明在资源的
36
+ `invariants` 中;每条约束只能比较同一记录的两个已声明字段,最多 20 条,
37
+ 由平台在 create/update/increment 的最终候选记录上统一执行。
134
38
 
135
39
  ```ts
136
40
  {
137
41
  code: 'sessions',
138
42
  name: '场次',
139
43
  fields: [
140
- { code: 'startAt', type: 'datetime', label: '开始时间', required: true },
141
- { code: 'endAt', type: 'datetime', label: '结束时间', required: true },
142
- { code: 'capacity', type: 'number.integer', label: '容量', required: true, min: 0 },
143
- { code: 'occupied', type: 'number.integer', label: '已占用', required: true, min: 0 },
44
+ { code: 'startAt', type: 'datetime', label: '开始', required: true },
45
+ { code: 'endAt', type: 'datetime', label: '结束', required: true },
46
+ { code: 'capacity', type: 'number.integer', label: '容量', min: 0 },
47
+ { code: 'occupied', type: 'number.integer', label: '已占用', min: 0 },
144
48
  ],
145
49
  invariants: [
146
50
  { code: 'time-order', expression: { leftField: 'startAt', operator: 'lt', rightField: 'endAt' } },
@@ -149,122 +53,10 @@ invariants when all must hold:
149
53
  }
150
54
  ```
151
55
 
152
- `datetime-range` is always a non-empty half-open interval `[start, end)`.
153
- Adjacent values such as `[10:00, 11:00)` and `[11:00, 12:00)` do not overlap.
154
- Use the ordinary `overlaps` query operator or a `query-empty` transaction guard;
155
- never add or subtract milliseconds at the boundary.
156
-
157
- Do not author raw schema or storage words as field types. `string`, `text`,
158
- `integer`, `decimal`, `boolean`, `date`, `datetime`, `uuid`, `json` and `file`
159
- describe value or storage families only when the platform protocol says so;
160
- their authored counterparts are the semantic catalog entries above (some names
161
- such as `date`, `datetime`, `uuid`, `json` and `file` intentionally coincide).
162
- In particular, `number` is not a field type: choose `number.integer` or
163
- `number.decimal`.
164
-
165
- Current-user row access has one spelling only:
166
-
167
- ```ts
168
- currentUserDataPolicy({
169
- code: 'reservation-host',
170
- name: '接待人员仅查看本人预约',
171
- resourceCode: 'visitor-reservations',
172
- field: 'hostUserId',
173
- roleCodes: ['reception_staff'],
174
- unrestrictedRoleCodes: ['visitor_admin'],
175
- })
176
- ```
177
-
178
- Put that value in `authz.dataPolicies`, declare both roles, and set the
179
- resource `dataPolicyCode` to the same code. Do not invent `current_user: true`,
180
- lowercase match modes, a dotted value path, or policy-level `roleCodes`. The
181
- compiler and platform know that `user.*` fields compare their stable `value`;
182
- `field` remains the declared field root code.
183
-
184
- For portal visibility windows, use the typed SDK expression and keep it a
185
- server-enforced read policy:
186
-
187
- ```ts
188
- resourceReadPolicy({
189
- code: 'published-articles',
190
- name: '仅查看当前已发布内容',
191
- resourceCode: 'articles',
192
- matchMode: 'AND',
193
- rules: [{ dimensionCode: 'organization', field: 'organizationId' }],
194
- expression: dataPolicyExpression.allOf(
195
- dataPolicyExpression.constant({
196
- field: 'status', operator: 'eq', value: 'PUBLISHED',
197
- }),
198
- dataPolicyExpression.databaseNow({
199
- field: 'publishAt', operator: 'lte',
200
- }),
201
- dataPolicyExpression.anyOf(
202
- dataPolicyExpression.null({ field: 'expireAt', operator: 'is_null' }),
203
- dataPolicyExpression.databaseNow({ field: 'expireAt', operator: 'gt' }),
204
- ),
205
- ),
206
- })
207
- ```
208
-
209
- `db_now` is platform-owned PostgreSQL statement time and only accepts a
210
- `datetime` field. Missing/null values match only `is_null`; they do not match
211
- negative constants or time comparisons. The same bounded expression can
212
- combine `currentUser`, `dimension`, `relation`, `constant`, `null`, and
213
- `databaseNow` leaves under `allOf`/`anyOf`. Never duplicate this expression in
214
- a page `where` filter and call it authorization: UI filters are optional
215
- display state and cannot weaken or replace Native RLS.
216
-
217
- `readExpression` is always added on top of the base `matchMode`/`rules`; those
218
- base rules continue to restrict create/update/delete. If writes truly need no
219
- row scope beyond capability checks, say so explicitly with
220
- `writeBoundary: 'capability_only'` instead of `matchMode`/`rules`. Never use an
221
- empty base implicitly: `check` rejects it so an AI cannot accidentally remove
222
- write authorization while adding a portal read window.
223
-
224
- When a business membership resource is the durable source of a package role or
225
- RelationshipGrant, declare the projection instead of calling authorization
226
- management endpoints from application code:
227
-
228
- ```ts
229
- authz: {
230
- // ...capabilities, roles and policies...
231
- roleMembershipSources: [{
232
- code: 'venue-managers',
233
- name: '场馆管理员角色成员',
234
- resourceCode: 'venue-manager-relations',
235
- userIdField: 'manager.value',
236
- roleCode: 'venue_admin',
237
- enabledField: 'enabled',
238
- failureMode: 'strict',
239
- }],
240
- relationshipGrantSources: [{
241
- code: 'venue-member-grants',
242
- name: '场馆成员关系授权',
243
- resourceCode: 'venue-manager-relations',
244
- subject: { type: 'user', userIdField: 'manager.value' },
245
- relationCode: 'member',
246
- targetResourceCode: 'venues',
247
- resourceIdField: 'venue.value',
248
- operations: ['read', 'update'],
249
- enabledField: 'enabled',
250
- failureMode: 'strict',
251
- }],
252
- }
253
- ```
254
-
255
- The user path must be `user.single.value`; the target path is `id` only when the
256
- source resource is also the target resource, or a `resource-ref.single.value`
257
- that points at the declared target resource.
258
- Operations are a bounded constant list. Create/update/delete the relationship
259
- resource through the standard Data API; the platform converges and revokes only
260
- the facts owned by that source. Projection failure is always strict because a
261
- last-known-good grant could defeat revocation.
56
+ `date-range` 与 `datetime-range` 必须显式声明 `rangeBoundary`,取值为 closed half-open。选择半开区间 `[start, end)` 时相邻时间段不冲突;闭区间端点相接可能重叠。
262
57
 
263
- Use the SDK projection health call after deployment. A platform operator with
264
- the existing authorization-management capability may run rebuild for historical
265
- rows or recover a dead-letter job. Rebuild/recovery are idempotent and accept an
266
- `operationId`; they never accept a user token, user id override or impersonation
267
- input. Do not write `sourceCode`, canonical membership rows or relationship
268
- grant rows yourself.
58
+ 业务 uuid 字段与系统 id 不同:可选业务 UUID 省略时为空,必填字段需调用方提供合法值,平台不会替业务 UUID 自动生成默认值。
269
59
 
270
- Run `pnpm openxiangda check` after every declaration or permission change.
60
+ 业务分派需要目标人员具有指定角色时,使用[事务中的角色条件](backend.md#role-member),
61
+ 由平台在写入事务中核对当前有效成员。候选查询、页面隐藏、应用管理员身份和历史
62
+ 角色列表都不能替代这一规则,也不应在应用中复制一份权限状态。
@@ -1,42 +1,112 @@
1
- # OpenXiangda 2.0 Delivery
2
-
3
- Run `pnpm openxiangda check --json`; it defaults to the test target, compiles
4
- the complete configuration and contract bundles, and asks the target
5
- platform's advertised `configurationCompatibility` endpoint to run the same
6
- Native compiler validation used during deployment preparation. This read-only
7
- preflight happens before workspace checks, tests, builds, Buildx or uploads.
8
- Use `--environment production` only when explicitly checking that target. A
9
- failure preserves `pointer`, client contract/schema versions, platform
10
- version/capability and the required/supported application-contract tuples.
11
- The capability also publishes the existing 4 MiB configuration, 8 MiB
12
- contract and 10 MiB total request bounds so oversized input fails before the
13
- transport layer.
14
- Never remove generated fields, reduce versions, raise limits, add app-code
15
- exceptions or introduce 1.x compatibility to make it pass. The platform
16
- revalidates authoritatively during deployment preparation.
17
-
18
- After compatibility succeeds, check owns static checks, tests and production
19
- builds but deliberately does not seal an AppPackage. Its
20
- `data.sealedArtifact` object and `.openxiangda/build/seal-status.json` make that
21
- state explicit even when an older `app-package.json` remains on disk. Deploy to
22
- test with the returned `openxiangda deploy` next command. Deploy owns the
23
- official application Dockerfile and platform-provided repository target,
24
- builds and pushes the Nest image, then writes a `sealed` status tied to the new
25
- immutable package digest. Never ask the developer for an image tag, digest,
26
- registry password, Docker configuration, or another public build command. If
27
- Docker, Buildx, repository configuration, or registry login is missing,
28
- preserve the stable machine error and retry with the same
29
- `pnpm openxiangda deploy` command after fixing that prerequisite.
30
-
31
- `pnpm openxiangda accept --plan <file>` is an optional manual preproduction
32
- test helper. It prepares expiring real identities but does not deploy, seal,
33
- promote or satisfy any release gate. Use it only when the requested acceptance
34
- needs real role membership and browser login.
35
-
36
- Use `pnpm openxiangda status` and `pnpm openxiangda logs` without an ID for the most recent run, or pass an explicit run ID.
37
-
38
- For the AI-native MCP entrypoint, `build_app` and `deployment_plan` are explicitly unsealed previews. After the user authorizes deployment, call `deploy_app` without any image coordinate; it owns the same automatic Buildx, push, digest and sealing path as the CLI.
39
-
40
- Applications with Native Data Resources automatically require `data.native-golden-crud`. If deployment returns `OPENXIANGDA_REQUIRED_CAPABILITY_UNAVAILABLE`, preserve the remediation to upgrade the platform and retry the same deploy command. Never remove the requirement, edit the AppPackage, construct a second identity path, or create Function-based CRUD.
41
-
42
- After a successful test run, deploy the exact same version with `pnpm openxiangda deploy --environment production --from <test-deployment-id>`. Roll back with `pnpm openxiangda rollback --environment production --to <app-version-id>`. The platform owns durable deployment state.
1
+ # 部署、生产晋级与恢复
2
+
3
+ 应用开发者从工作区执行 `pnpm openxiangda`。平台负责应用版本、运行状态和恢复决定。工具链自身的 npm 发布由平台维护者负责,应用项目无需复制发包脚本或平台验证矩阵。
4
+
5
+ ## 测试部署
6
+
7
+ 发布前,先将本轮源码、生成契约及必要记录合入并推送仓库的远端默认主分支,然后从干净且同步的主分支工作区发布。工具从 origin 的远端 HEAD 识别主分支,不把任务分支的 upstream 当作主线。未提交、未推送、未合并或落后主线的问题会在构建和上传前返回;工具不会自动合并分支或覆盖其他会话的改动。
8
+
9
+ 开发开始时先同步主线并读取项目现状,开发完成包括提交、推送与主线整合。每个工作区保持一个写者;需要并行时使用独立目录并明确各任务范围。日常 dev/check 仍可验证未提交源码。没有 Git 远端的项目应先建立并绑定仓库再发布。
10
+
11
+ 准备部署时直接执行 deploy,它已经包含兼容性预检、生成、检查、测试和构建。只想检查代码时使用 [check](testing.md),无需在 deploy 前重复运行全套检查。
12
+
13
+ 测试发布还会核对 [AppSpec](appspec.md) 的需求依据、架构、权限、性能预算和验收计划。缺失时给出具体记录位置,先补实际设计;首次测试部署不要求预先完成线上业务验收。
14
+
15
+ ```bash
16
+ pnpm openxiangda deploy --dry-run --json
17
+ pnpm openxiangda deploy
18
+ pnpm openxiangda status --json
19
+ pnpm openxiangda logs <deployment-id> --json
20
+ ```
21
+
22
+ 默认目标为 `test`,平台内部标识为 `preproduction`。只读预览不生成构建产物、不上传制品、不提交 DeploymentRun;因此预览成功不能证明代码已通过检查。正式检查使用本地完整校验,再通过目标平台的 `configurationCompatibility` 核对同源规则、密钥、已有物理模型和登录提供方等只读条件;失败时停止后续步骤。只有启用了自定义 Nest 后端的应用才需要构建后端镜像及对应 Docker 环境。
23
+
24
+ 平台启用镜像上传后,`deploy` 在本机用 Docker Buildx 导出 OCI 镜像,再使用当前平台登录态分片上传。开发者只需应用部署权限,无需登录平台管理员的镜像仓库或取得推送凭据。已完成的镜像层按摘要复用,中断后重新执行同一条部署命令可恢复上传;平台核验镜像完整性后才进入应用发布。尚未升级的旧平台保留其原镜像构建合同;新平台已声明上传能力但未启用时,命令会明确报告平台配置缺失。
25
+
26
+ 生成的不可变 AppVersion 绑定前端、可选后端、配置契约和制品摘要。默认提交后持续跟踪同一运行,直到平台成功、失败或取消,最多观察 15 分钟。`--no-wait` 只提交,适用于已有状态跟踪器的自动化;此时返回运行 ID 不代表部署完成。
27
+
28
+ 构建、上传及平台执行都会显示当前阶段和耗时,长步骤每 10 秒反馈一次。平台的准备、部署、切换和健康检查状态来自原运行。观察超时或连接中断不会取消部署或重建候选,使用下面的命令继续跟踪:
29
+
30
+ ```bash
31
+ pnpm openxiangda status <deployment-id> --watch
32
+ ```
33
+
34
+ 网络响应不确定时先查询原运行,不凭本地输出创建重复部署。平台部署成功后,仍需执行真实角色的业务验收。
35
+
36
+ 相同源码候选重试时,工具会重新核对本地验证证据、封存清单和制品字节,复用仍有效的构建结果及后端镜像;已上传内容按摘要查询并复用。只有环境条件改变时不需要重建源码制品。输出损坏、输入变化或缓存缺失时自动回到正式检查和构建。缓存位于 `.openxiangda/build/`,不是新的部署状态源;提交响应不确定时使用原候选和幂等键,已有失败运行按其 recovery 恢复。
37
+
38
+ ## 测试环境验收
39
+
40
+ 至少记录应用版本、目标环境、真实角色、复现数据、预期和实际结果。按改动范围检查页面、权限、业务规则和失败路径;详见[校验与验收](testing.md)。
41
+
42
+ | 证据 | 能说明什么 |
43
+ | --- | --- |
44
+ | 本地 check 成功 | 本次声明兼容,检查、测试和构建通过 |
45
+ | 包密封完成 | 存在可识别的不可变候选版本 |
46
+ | DeploymentRun 成功 | 平台完成该版本的部署流程 |
47
+ | 真实角色的页面与业务操作通过 | 对应场景在目标环境可用 |
48
+ | 生产晋级成功并回读 | 生产使用指定测试版本;仍需核对实际入口与关键业务 |
49
+
50
+ 构建成功、提交成功和真实业务验收是不同证据,报告时分别给出实际状态。
51
+
52
+ ## 生产晋级
53
+
54
+ 生产必须复用已成功部署到测试环境的同一版本,不能从当前源码直接重建:
55
+
56
+ 先按真实操作保存 `appspec/verification/<测试运行ID>.json` 并提交、推送到主线。晋级会从测试源码提交读取原验收计划,核对报告的运行 ID、包摘要、AC 场景与性能证据;主线后来的需求不改变已测范围。
57
+
58
+ 该版本的源码提交必须仍包含在权威远端主分支中。主分支后来有新提交,不会改变本次晋级的制品。若任务分支采用 squash/rebase 合并,应在最终主线提交上重新冻结并验证测试候选,不能继续晋级合并前的提交。
59
+
60
+ ```bash
61
+ pnpm openxiangda deploy --environment production --from <test-deployment-id> --dry-run --json
62
+ pnpm openxiangda deploy --environment production --from <test-deployment-id>
63
+ pnpm openxiangda status --json
64
+ ```
65
+
66
+ 预览会精确读取指定测试运行的封存配置并核对生产密钥、模型和登录条件,返回源运行、版本与摘要。主线后来变化或版本较旧,不会使预检改用当前源码或最近版本列表。测试运行失败、版本缺失或条件不符时直接失败。平台在真正晋级时再次权威校验。生产参数不接受测试环境的 `environmentId` 或 `idempotencyKey`。已有生产发布授权时可继续执行;授权不明确时先准备版本、预览及验收证据,再确认具体发布对象。
67
+
68
+ ## 失败、重试与回滚
69
+
70
+ `logs` 返回首个失败 `rootFailure`、最近失败 `latestFailure`、候选状态、尝试账本与 `recovery`。无失败时对应字段为 null。按平台给出的 `recovery.nextCommand` 处理;只在 `recovery.cancelAllowed` 为真时取消。已激活的运行不能用 cancel 撤销。
71
+
72
+ ```bash
73
+ pnpm openxiangda retry <deployment-id>
74
+ pnpm openxiangda cancel <deployment-id>
75
+ pnpm openxiangda rollback --to <app-version-id>
76
+ ```
77
+
78
+ 环境版本回滚不保证撤销数据库业务写入;数据修复需要单独计划与验证。暂停与恢复默认作用于测试环境;生产必须显式选择:
79
+
80
+ ```bash
81
+ pnpm openxiangda stop
82
+ pnpm openxiangda start
83
+ pnpm openxiangda stop --environment production
84
+ ```
85
+
86
+ stop 保留数据与配置,start 从当前不可变版本恢复。不要把暂停、取消、回滚当作同一种操作。
87
+
88
+ ## 自动化与错误定位
89
+
90
+ CLI 的 `--json` 输出单个 `openxiangda.cli-result/v2` 对象;失败包含 code、message、retryable、remediation、nextCommand,以及适用的 pointer/details。自动化依据 code 和结构化字段决策,不解析中文描述。
91
+
92
+ `check`、`deploy` 和持续状态观察的结果带 `data.execution`,包含本次操作 ID、总耗时及各阶段状态和耗时。阶段进度写到 stderr,保持 `--json` 的 stdout 可解析;`--json-events` 则通过 `command.status` 返回同源结构化进度。MCP 客户端提供 `progressToken` 时收到标准进度通知;不订阅通知仍能从最终结果读取阶段摘要。MCP `deploy_app.wait` 默认 true,`deployment_status.watch` 可继续观察原运行。
93
+
94
+ 兼容性错误会列出当前工具链与目标平台的版本、能力和契约要求。按定位修复声明或升级目标平台,不删除真实业务要求、改写摘要或绕过权限来让预检通过。应用所需能力由规范化声明派生,应用不能手写一份能力列表冒充平台支持。
95
+
96
+ `OPENXIANGDA_CONFIGURATION_VALIDATOR_MISMATCH` 表示工具链与平台的校验实现不配套,应按发布说明升级对应版本;它会在构建、镜像推送和制品上传前出现。模型类型不能原地替换时,按提示设计新字段及数据转换;必需密钥缺失时配置目标环境后继续,不修改源码伪装问题已解决。
97
+
98
+ MCP 的 check_app、deployment_plan、deploy_app 使用与 CLI 相同的环境与生产晋级参数规则。完整参数以[CLI](cli.md)与[MCP](mcp.md)为准。
99
+
100
+ ## 构建前运行配额
101
+
102
+ `deploy --dry-run`(MCP `deployment_plan`)会只读查询目标 TEST 的运行配额,输出 `runtimeCapacity` 的核验时间、所需增量、各配额剩余量和缺口。`sufficient: false` 表示当前不足;`null` 表示无需新增或未核验,必须结合 `basis` 与 `capacity.checked` 阅读。专用命名空间未检查不能当成资源充足。
103
+
104
+ 正式 deploy 在检查脚本和镜像构建前预检;平台缺少配套能力或无法核验时明确停止。配额快照不预留资源,实际执行再次检查。已有可验证密封候选会携带摘要和幂等键,平台识别 `existing-run` 时返回原运行,不把它当作新副本;观察或恢复原运行使用 status/retry。不要为绕过配额创建新包或切换目标环境。
105
+
106
+ ## TEST 单副本维护替换
107
+
108
+ 平台配额只允许一个后端副本时,可显式选择维护替换。它会停止当前 TEST 后端,期间应用不可用;成功后激活新版本,失败时由原 DeploymentRun 恢复旧后端。恢复尚未完成时继续占用原运行,status/logs 显示恢复阶段与首个失败,不允许用新部署或取消跳过恢复。
109
+
110
+ 先运行 `openxiangda deploy --environment test --strategy maintenance-replace --dry-run --json` 查看前驱版本、Head revision 和停止后的容量估算,再使用相同参数去掉 `--dry-run` 提交。计划不预留资源。只有已存在、身份匹配的单副本 TEST 后端才可使用;前端应用、新应用和 production 不支持。默认仍为 rolling,不会因配额不足自动停止实例。
111
+
112
+ 策略属于部署幂等请求。默认维护幂等键含策略,显式幂等键不能在不同策略之间复用。已有运行通过 status/retry 恢复;持续恢复中的运行保持 preparing/maintenance-recovery-required,平台会重试恢复,恢复失败时保留原运行与错误。
@@ -0,0 +1,32 @@
1
+ # 需求与开发流程
2
+
3
+ 先确认用户要完成的任务,再选择数据模型、页面和权限。使用项目锁定的 `pnpm openxiangda`,读取 `context --json` 与相关专题。现有项目的代码和实时契约优先于其他项目的样例。
4
+
5
+ ## 新应用先完成设计基线
6
+
7
+ 从模糊想法开始时,按[对话发现与产品设计](product-design.md)分析已有资料、提出可解释的模块建议,用少量自然语言问题持续沟通确认。完整首发范围的 PRD、旅程、页面交互、原型、权限和架构设计齐备后,再制定实施计划和编写业务实现。用户已给出完整材料时先核对矛盾和遗漏,不重复访谈;已有确认持续有效。
8
+
9
+ ## 根据任务决定工作量 {#risk}
10
+
11
+ | 变化 | 需要明确的内容 | 验证 |
12
+ | --- | --- | --- |
13
+ | 格式、无行为重构 | 保留现有行为,无需创建需求记录 | 受影响静态检查和现有测试 |
14
+ | 文案、字段展示、局部规则 | 业务含义、影响页面和预期结果 | 对应数据和交互 |
15
+ | 跨模型、权限、状态变化 | 用户角色、正反场景、数据和恢复边界 | 真实角色、API 和浏览器 |
16
+ | 身份、迁移、并发、外部副作用 | 所有者、失败与幂等、资源边界、回滚和架构决定 | 所涉及契约的专项验证 |
17
+
18
+ 技术命名、可逆布局等在已有要求内决定。新的业务含义、权限扩大或尚未授权的外部操作需要用户决定;已经明确授权的范围不重复询问。用户只要求分析时,不自动创建应用或发布。
19
+
20
+ ## 选择平台能力 {#capabilities}
21
+
22
+ - 普通数据管理:通过 `defineDataModel`、`defineApplicationModule` 和显式 CRUD 视图声明;模型不自动生成菜单或写权限。
23
+ - PC/移动页面:先复用平台组件和标准页面,再使用受支持的页面、插槽与导航扩展。详见[前端](frontend.md)。
24
+ - 无平台账号的外部表单:使用[匿名公开访问](public-access.md),不用普通 RBAC 角色冒充匿名主体。
25
+ - 标准审批、待办与通知:按需声明平台能力,见[工作流](workflow-events.md)。
26
+ - 真实事务或外部集成:使用[按需后端](backend.md),不为每张表重写 CRUD 控制器。
27
+
28
+ ## 实施与交接 {#iteration}
29
+
30
+ 开发使用 `pnpm openxiangda dev`,过程中运行必要的聚焦测试。交接前按[检查与验收](testing.md)验证;授权发布后按[交付](delivery.md)部署。失败保留错误码、位置和原始候选,依据平台恢复指令继续。
31
+
32
+ [AppSpec](appspec.md)保存业务意图、设计与交付记录,不复制字段 Schema、生成契约和部署状态。新应用维护具体设计和评审,复杂度随业务展开;既有小变更沿用有效设计,仅修订受影响记录,无行为变化可引用已有记录。每轮先读取当前规则、澄清业务、评估架构、权限和性能,随后实施与验证,发布后更新当前规格及交接。测试部署前需要设计和验收计划,生产晋级前需要绑定测试运行及包摘要的实际验收报告。