openxiangda-skill-kit 2.0.0-alpha.98 → 2.0.0
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/LICENSE +21 -0
- package/README.md +2 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -19
- package/dist/index.js.map +1 -1
- package/dist/workspace-guidance.d.ts +13 -0
- package/dist/workspace-guidance.d.ts.map +1 -0
- package/dist/workspace-guidance.js +67 -0
- package/dist/workspace-guidance.js.map +1 -0
- package/package.json +12 -3
- package/skills/manifest.json +2 -2
- package/skills/openxiangda-v2/SKILL.md +63 -46
- package/skills/openxiangda-v2/agents/openai.yaml +2 -2
- package/skills/openxiangda-v2/references/administration.md +27 -0
- package/skills/openxiangda-v2/references/application-foundation.md +162 -0
- package/skills/openxiangda-v2/references/appspec.md +132 -47
- package/skills/openxiangda-v2/references/backend.md +101 -237
- package/skills/openxiangda-v2/references/cli.md +27 -0
- package/skills/openxiangda-v2/references/concepts.md +61 -0
- package/skills/openxiangda-v2/references/data-authz.md +36 -239
- package/skills/openxiangda-v2/references/delivery.md +110 -42
- package/skills/openxiangda-v2/references/development.md +32 -0
- package/skills/openxiangda-v2/references/field-components.md +236 -0
- package/skills/openxiangda-v2/references/frontend.md +269 -101
- package/skills/openxiangda-v2/references/getting-started.md +66 -0
- package/skills/openxiangda-v2/references/interaction-patterns.md +56 -0
- package/skills/openxiangda-v2/references/mcp.md +649 -0
- package/skills/openxiangda-v2/references/product-design.md +142 -0
- package/skills/openxiangda-v2/references/public-access.md +167 -0
- package/skills/openxiangda-v2/references/testing.md +45 -48
- package/skills/openxiangda-v2/references/upgrading.md +39 -0
- package/skills/openxiangda-v2/references/workflow-events.md +152 -138
- package/skills/openxiangda-v2/references/architecture.md +0 -7
- package/skills/openxiangda-v2/references/commands.md +0 -21
- package/skills/openxiangda-v2/references/discovery.md +0 -15
- package/skills/openxiangda-v2/references/workspace.md +0 -25
|
@@ -1,141 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Data API 与权限
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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');
|
|
19
|
-
|
|
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
|
-
```
|
|
5
|
+
以下仅以仪器管理应用举例,不是平台默认模型或角色。该示例的行规则是:学校管理员不受行谓词限制;学院管理员按记录 `collegeId` 匹配;仪器管理员按 `instrumentAdminIds` 包含当前用户匹配。不要增加影子范围字段。
|
|
56
6
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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.
|
|
7
|
+
`collegeId` 是应用 `colleges` Native Resource 的记录 UUID。学院 scope dimension 通过
|
|
8
|
+
`valueSource.kind=native_resource` 绑定同一资源,选择器只展示平台按当前 membership 与
|
|
9
|
+
create/update 闭包返回的值。人员和部门保存平台目录真实 ID;部门不等于学院,也不能作为
|
|
10
|
+
学院范围的隐式来源。
|
|
64
11
|
|
|
65
|
-
|
|
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.
|
|
12
|
+
字段策略支持 `read`、`create`、`update` 和 `mask`。显式空数组拒绝,能力数组采用 all-of。无权更新字段不仅 disabled,还必须从更新 payload 删除。
|
|
74
13
|
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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.
|
|
104
|
-
|
|
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.
|
|
109
|
-
|
|
110
|
-
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.
|
|
111
|
-
|
|
112
|
-
Generated list, detail, audit and preview surfaces render the stored canonical
|
|
113
|
-
`label` snapshots for `option.*`, `user.*`, `department.*` and
|
|
114
|
-
`resource-ref.*`, including multiple arrays and the first linked list column.
|
|
115
|
-
Do not add application formatters, directory re-queries or browser-side joins
|
|
116
|
-
for these standard fields.
|
|
18
|
+
角色成员、维度授权和平台管理员由平台管理面维护,不属于应用开发 CLI。
|
|
117
19
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
`
|
|
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)。
|
|
121
29
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
30
|
+
匿名外部访问不属于 RBAC 角色或 current-user 行策略。公开表单、续填、附件、重复校验和同一
|
|
31
|
+
浏览器的本人记录访问只通过[`frontend.publicAccess` 专用合同](public-access.md)开放;平台继续在
|
|
32
|
+
专用端点和 PostgreSQL/RLS 中强制匿名主体、字段、策略及提交回执边界。
|
|
125
33
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
invariants
|
|
34
|
+
数值边界直接声明在字段上,`min`/`max` 为闭区间,并且只允许用于
|
|
35
|
+
`number.integer` 和 `number.decimal`。跨字段约束声明在资源的
|
|
36
|
+
`invariants` 中;每条约束只能比较同一记录的两个已声明字段,最多 20 条,
|
|
37
|
+
由平台在 create/update/increment 的最终候选记录上统一执行。
|
|
129
38
|
|
|
130
39
|
```ts
|
|
131
40
|
{
|
|
132
41
|
code: 'sessions',
|
|
133
42
|
name: '场次',
|
|
134
43
|
fields: [
|
|
135
|
-
{ code: 'startAt', type: 'datetime', label: '
|
|
136
|
-
{ code: 'endAt', type: 'datetime', label: '
|
|
137
|
-
{ code: 'capacity', type: 'number.integer', label: '容量',
|
|
138
|
-
{ code: 'occupied', type: 'number.integer', label: '已占用',
|
|
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 },
|
|
139
48
|
],
|
|
140
49
|
invariants: [
|
|
141
50
|
{ code: 'time-order', expression: { leftField: 'startAt', operator: 'lt', rightField: 'endAt' } },
|
|
@@ -144,122 +53,10 @@ invariants when all must hold:
|
|
|
144
53
|
}
|
|
145
54
|
```
|
|
146
55
|
|
|
147
|
-
`datetime-range`
|
|
148
|
-
Adjacent values such as `[10:00, 11:00)` and `[11:00, 12:00)` do not overlap.
|
|
149
|
-
Use the ordinary `overlaps` query operator or a `query-empty` transaction guard;
|
|
150
|
-
never add or subtract milliseconds at the boundary.
|
|
151
|
-
|
|
152
|
-
Do not author raw schema or storage words as field types. `string`, `text`,
|
|
153
|
-
`integer`, `decimal`, `boolean`, `date`, `datetime`, `uuid`, `json` and `file`
|
|
154
|
-
describe value or storage families only when the platform protocol says so;
|
|
155
|
-
their authored counterparts are the semantic catalog entries above (some names
|
|
156
|
-
such as `date`, `datetime`, `uuid`, `json` and `file` intentionally coincide).
|
|
157
|
-
In particular, `number` is not a field type: choose `number.integer` or
|
|
158
|
-
`number.decimal`.
|
|
159
|
-
|
|
160
|
-
Current-user row access has one spelling only:
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
currentUserDataPolicy({
|
|
164
|
-
code: 'reservation-host',
|
|
165
|
-
name: '接待人员仅查看本人预约',
|
|
166
|
-
resourceCode: 'visitor-reservations',
|
|
167
|
-
field: 'hostUserId',
|
|
168
|
-
roleCodes: ['reception_staff'],
|
|
169
|
-
unrestrictedRoleCodes: ['visitor_admin'],
|
|
170
|
-
})
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Put that value in `authz.dataPolicies`, declare both roles, and set the
|
|
174
|
-
resource `dataPolicyCode` to the same code. Do not invent `current_user: true`,
|
|
175
|
-
lowercase match modes, a dotted value path, or policy-level `roleCodes`. The
|
|
176
|
-
compiler and platform know that `user.*` fields compare their stable `value`;
|
|
177
|
-
`field` remains the declared field root code.
|
|
178
|
-
|
|
179
|
-
For portal visibility windows, use the typed SDK expression and keep it a
|
|
180
|
-
server-enforced read policy:
|
|
181
|
-
|
|
182
|
-
```ts
|
|
183
|
-
resourceReadPolicy({
|
|
184
|
-
code: 'published-articles',
|
|
185
|
-
name: '仅查看当前已发布内容',
|
|
186
|
-
resourceCode: 'articles',
|
|
187
|
-
matchMode: 'AND',
|
|
188
|
-
rules: [{ dimensionCode: 'organization', field: 'organizationId' }],
|
|
189
|
-
expression: dataPolicyExpression.allOf(
|
|
190
|
-
dataPolicyExpression.constant({
|
|
191
|
-
field: 'status', operator: 'eq', value: 'PUBLISHED',
|
|
192
|
-
}),
|
|
193
|
-
dataPolicyExpression.databaseNow({
|
|
194
|
-
field: 'publishAt', operator: 'lte',
|
|
195
|
-
}),
|
|
196
|
-
dataPolicyExpression.anyOf(
|
|
197
|
-
dataPolicyExpression.null({ field: 'expireAt', operator: 'is_null' }),
|
|
198
|
-
dataPolicyExpression.databaseNow({ field: 'expireAt', operator: 'gt' }),
|
|
199
|
-
),
|
|
200
|
-
),
|
|
201
|
-
})
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
`db_now` is platform-owned PostgreSQL statement time and only accepts a
|
|
205
|
-
`datetime` field. Missing/null values match only `is_null`; they do not match
|
|
206
|
-
negative constants or time comparisons. The same bounded expression can
|
|
207
|
-
combine `currentUser`, `dimension`, `relation`, `constant`, `null`, and
|
|
208
|
-
`databaseNow` leaves under `allOf`/`anyOf`. Never duplicate this expression in
|
|
209
|
-
a page `where` filter and call it authorization: UI filters are optional
|
|
210
|
-
display state and cannot weaken or replace Native RLS.
|
|
211
|
-
|
|
212
|
-
`readExpression` is always added on top of the base `matchMode`/`rules`; those
|
|
213
|
-
base rules continue to restrict create/update/delete. If writes truly need no
|
|
214
|
-
row scope beyond capability checks, say so explicitly with
|
|
215
|
-
`writeBoundary: 'capability_only'` instead of `matchMode`/`rules`. Never use an
|
|
216
|
-
empty base implicitly: `check` rejects it so an AI cannot accidentally remove
|
|
217
|
-
write authorization while adding a portal read window.
|
|
218
|
-
|
|
219
|
-
When a business membership resource is the durable source of a package role or
|
|
220
|
-
RelationshipGrant, declare the projection instead of calling authorization
|
|
221
|
-
management endpoints from application code:
|
|
222
|
-
|
|
223
|
-
```ts
|
|
224
|
-
authz: {
|
|
225
|
-
// ...capabilities, roles and policies...
|
|
226
|
-
roleMembershipSources: [{
|
|
227
|
-
code: 'venue-managers',
|
|
228
|
-
name: '场馆管理员角色成员',
|
|
229
|
-
resourceCode: 'venue-manager-relations',
|
|
230
|
-
userIdField: 'manager.value',
|
|
231
|
-
roleCode: 'venue_admin',
|
|
232
|
-
enabledField: 'enabled',
|
|
233
|
-
failureMode: 'strict',
|
|
234
|
-
}],
|
|
235
|
-
relationshipGrantSources: [{
|
|
236
|
-
code: 'venue-member-grants',
|
|
237
|
-
name: '场馆成员关系授权',
|
|
238
|
-
resourceCode: 'venue-manager-relations',
|
|
239
|
-
subject: { type: 'user', userIdField: 'manager.value' },
|
|
240
|
-
relationCode: 'member',
|
|
241
|
-
targetResourceCode: 'venues',
|
|
242
|
-
resourceIdField: 'venue.value',
|
|
243
|
-
operations: ['read', 'update'],
|
|
244
|
-
enabledField: 'enabled',
|
|
245
|
-
failureMode: 'strict',
|
|
246
|
-
}],
|
|
247
|
-
}
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
The user path must be `user.single.value`; the target path is `id` only when the
|
|
251
|
-
source resource is also the target resource, or a `resource-ref.single.value`
|
|
252
|
-
that points at the declared target resource.
|
|
253
|
-
Operations are a bounded constant list. Create/update/delete the relationship
|
|
254
|
-
resource through the standard Data API; the platform converges and revokes only
|
|
255
|
-
the facts owned by that source. Projection failure is always strict because a
|
|
256
|
-
last-known-good grant could defeat revocation.
|
|
56
|
+
`date-range` 与 `datetime-range` 必须显式声明 `rangeBoundary`,取值为 closed 或 half-open。选择半开区间 `[start, end)` 时相邻时间段不冲突;闭区间端点相接可能重叠。
|
|
257
57
|
|
|
258
|
-
|
|
259
|
-
the existing authorization-management capability may run rebuild for historical
|
|
260
|
-
rows or recover a dead-letter job. Rebuild/recovery are idempotent and accept an
|
|
261
|
-
`operationId`; they never accept a user token, user id override or impersonation
|
|
262
|
-
input. Do not write `sourceCode`, canonical membership rows or relationship
|
|
263
|
-
grant rows yourself.
|
|
58
|
+
业务 uuid 字段与系统 id 不同:可选业务 UUID 省略时为空,必填字段需调用方提供合法值,平台不会替业务 UUID 自动生成默认值。
|
|
264
59
|
|
|
265
|
-
|
|
60
|
+
业务分派需要目标人员具有指定角色时,使用[事务中的角色条件](backend.md#role-member),
|
|
61
|
+
由平台在写入事务中核对当前有效成员。候选查询、页面隐藏、应用管理员身份和历史
|
|
62
|
+
角色列表都不能替代这一规则,也不应在应用中复制一份权限状态。
|
|
@@ -1,42 +1,110 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
test
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
+
生成的不可变 AppVersion 绑定前端、可选后端、配置契约和制品摘要。默认提交后持续跟踪同一运行,直到平台成功、失败或取消,最多观察 15 分钟。`--no-wait` 只提交,适用于已有状态跟踪器的自动化;此时返回运行 ID 不代表部署完成。
|
|
25
|
+
|
|
26
|
+
构建、上传及平台执行都会显示当前阶段和耗时,长步骤每 10 秒反馈一次。平台的准备、部署、切换和健康检查状态来自原运行。观察超时或连接中断不会取消部署或重建候选,使用下面的命令继续跟踪:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm openxiangda status <deployment-id> --watch
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
网络响应不确定时先查询原运行,不凭本地输出创建重复部署。平台部署成功后,仍需执行真实角色的业务验收。
|
|
33
|
+
|
|
34
|
+
相同源码候选重试时,工具会重新核对本地验证证据、封存清单和制品字节,复用仍有效的构建结果及后端镜像;已上传内容按摘要查询并复用。只有环境条件改变时不需要重建源码制品。输出损坏、输入变化或缓存缺失时自动回到正式检查和构建。缓存位于 `.openxiangda/build/`,不是新的部署状态源;提交响应不确定时使用原候选和幂等键,已有失败运行按其 recovery 恢复。
|
|
35
|
+
|
|
36
|
+
## 测试环境验收
|
|
37
|
+
|
|
38
|
+
至少记录应用版本、目标环境、真实角色、复现数据、预期和实际结果。按改动范围检查页面、权限、业务规则和失败路径;详见[校验与验收](testing.md)。
|
|
39
|
+
|
|
40
|
+
| 证据 | 能说明什么 |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| 本地 check 成功 | 本次声明兼容,检查、测试和构建通过 |
|
|
43
|
+
| 包密封完成 | 存在可识别的不可变候选版本 |
|
|
44
|
+
| DeploymentRun 成功 | 平台完成该版本的部署流程 |
|
|
45
|
+
| 真实角色的页面与业务操作通过 | 对应场景在目标环境可用 |
|
|
46
|
+
| 生产晋级成功并回读 | 生产使用指定测试版本;仍需核对实际入口与关键业务 |
|
|
47
|
+
|
|
48
|
+
构建成功、提交成功和真实业务验收是不同证据,报告时分别给出实际状态。
|
|
49
|
+
|
|
50
|
+
## 生产晋级
|
|
51
|
+
|
|
52
|
+
生产必须复用已成功部署到测试环境的同一版本,不能从当前源码直接重建:
|
|
53
|
+
|
|
54
|
+
先按真实操作保存 `appspec/verification/<测试运行ID>.json` 并提交、推送到主线。晋级会从测试源码提交读取原验收计划,核对报告的运行 ID、包摘要、AC 场景与性能证据;主线后来的需求不改变已测范围。
|
|
55
|
+
|
|
56
|
+
该版本的源码提交必须仍包含在权威远端主分支中。主分支后来有新提交,不会改变本次晋级的制品。若任务分支采用 squash/rebase 合并,应在最终主线提交上重新冻结并验证测试候选,不能继续晋级合并前的提交。
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pnpm openxiangda deploy --environment production --from <test-deployment-id> --dry-run --json
|
|
60
|
+
pnpm openxiangda deploy --environment production --from <test-deployment-id>
|
|
61
|
+
pnpm openxiangda status --json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
预览会精确读取指定测试运行的封存配置并核对生产密钥、模型和登录条件,返回源运行、版本与摘要。主线后来变化或版本较旧,不会使预检改用当前源码或最近版本列表。测试运行失败、版本缺失或条件不符时直接失败。平台在真正晋级时再次权威校验。生产参数不接受测试环境的 `environmentId` 或 `idempotencyKey`。已有生产发布授权时可继续执行;授权不明确时先准备版本、预览及验收证据,再确认具体发布对象。
|
|
65
|
+
|
|
66
|
+
## 失败、重试与回滚
|
|
67
|
+
|
|
68
|
+
`logs` 返回首个失败 `rootFailure`、最近失败 `latestFailure`、候选状态、尝试账本与 `recovery`。无失败时对应字段为 null。按平台给出的 `recovery.nextCommand` 处理;只在 `recovery.cancelAllowed` 为真时取消。已激活的运行不能用 cancel 撤销。
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pnpm openxiangda retry <deployment-id>
|
|
72
|
+
pnpm openxiangda cancel <deployment-id>
|
|
73
|
+
pnpm openxiangda rollback --to <app-version-id>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
环境版本回滚不保证撤销数据库业务写入;数据修复需要单独计划与验证。暂停与恢复默认作用于测试环境;生产必须显式选择:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pnpm openxiangda stop
|
|
80
|
+
pnpm openxiangda start
|
|
81
|
+
pnpm openxiangda stop --environment production
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
stop 保留数据与配置,start 从当前不可变版本恢复。不要把暂停、取消、回滚当作同一种操作。
|
|
85
|
+
|
|
86
|
+
## 自动化与错误定位
|
|
87
|
+
|
|
88
|
+
CLI 的 `--json` 输出单个 `openxiangda.cli-result/v2` 对象;失败包含 code、message、retryable、remediation、nextCommand,以及适用的 pointer/details。自动化依据 code 和结构化字段决策,不解析中文描述。
|
|
89
|
+
|
|
90
|
+
`check`、`deploy` 和持续状态观察的结果带 `data.execution`,包含本次操作 ID、总耗时及各阶段状态和耗时。阶段进度写到 stderr,保持 `--json` 的 stdout 可解析;`--json-events` 则通过 `command.status` 返回同源结构化进度。MCP 客户端提供 `progressToken` 时收到标准进度通知;不订阅通知仍能从最终结果读取阶段摘要。MCP `deploy_app.wait` 默认 true,`deployment_status.watch` 可继续观察原运行。
|
|
91
|
+
|
|
92
|
+
兼容性错误会列出当前工具链与目标平台的版本、能力和契约要求。按定位修复声明或升级目标平台,不删除真实业务要求、改写摘要或绕过权限来让预检通过。应用所需能力由规范化声明派生,应用不能手写一份能力列表冒充平台支持。
|
|
93
|
+
|
|
94
|
+
`OPENXIANGDA_CONFIGURATION_VALIDATOR_MISMATCH` 表示工具链与平台的校验实现不配套,应按发布说明升级对应版本;它会在构建、镜像推送和制品上传前出现。模型类型不能原地替换时,按提示设计新字段及数据转换;必需密钥缺失时配置目标环境后继续,不修改源码伪装问题已解决。
|
|
95
|
+
|
|
96
|
+
MCP 的 check_app、deployment_plan、deploy_app 使用与 CLI 相同的环境与生产晋级参数规则。完整参数以[CLI](cli.md)与[MCP](mcp.md)为准。
|
|
97
|
+
|
|
98
|
+
## 构建前运行配额
|
|
99
|
+
|
|
100
|
+
`deploy --dry-run`(MCP `deployment_plan`)会只读查询目标 TEST 的运行配额,输出 `runtimeCapacity` 的核验时间、所需增量、各配额剩余量和缺口。`sufficient: false` 表示当前不足;`null` 表示无需新增或未核验,必须结合 `basis` 与 `capacity.checked` 阅读。专用命名空间未检查不能当成资源充足。
|
|
101
|
+
|
|
102
|
+
正式 deploy 在检查脚本和镜像构建前预检;平台缺少配套能力或无法核验时明确停止。配额快照不预留资源,实际执行再次检查。已有可验证密封候选会携带摘要和幂等键,平台识别 `existing-run` 时返回原运行,不把它当作新副本;观察或恢复原运行使用 status/retry。不要为绕过配额创建新包或切换目标环境。
|
|
103
|
+
|
|
104
|
+
## TEST 单副本维护替换
|
|
105
|
+
|
|
106
|
+
平台配额只允许一个后端副本时,可显式选择维护替换。它会停止当前 TEST 后端,期间应用不可用;成功后激活新版本,失败时由原 DeploymentRun 恢复旧后端。恢复尚未完成时继续占用原运行,status/logs 显示恢复阶段与首个失败,不允许用新部署或取消跳过恢复。
|
|
107
|
+
|
|
108
|
+
先运行 `openxiangda deploy --environment test --strategy maintenance-replace --dry-run --json` 查看前驱版本、Head revision 和停止后的容量估算,再使用相同参数去掉 `--dry-run` 提交。计划不预留资源。只有已存在、身份匹配的单副本 TEST 后端才可使用;前端应用、新应用和 production 不支持。默认仍为 rolling,不会因配额不足自动停止实例。
|
|
109
|
+
|
|
110
|
+
策略属于部署幂等请求。默认维护幂等键含策略,显式幂等键不能在不同策略之间复用。已有运行通过 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、生成契约和部署状态。新应用维护具体设计和评审,复杂度随业务展开;既有小变更沿用有效设计,仅修订受影响记录,无行为变化可引用已有记录。每轮先读取当前规则、澄清业务、评估架构、权限和性能,随后实施与验证,发布后更新当前规格及交接。测试部署前需要设计和验收计划,生产晋级前需要绑定测试运行及包摘要的实际验收报告。
|