openxiangda 2.0.0-alpha.99 → 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.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -20
  3. package/bin/distribution/commands.js +55 -0
  4. package/bin/distribution/launcher.js +49 -0
  5. package/bin/distribution/migrate.js +60 -0
  6. package/bin/distribution/releases.js +52 -0
  7. package/bin/distribution/skills.js +80 -0
  8. package/bin/distribution/update.js +68 -0
  9. package/bin/distribution/workspace.js +85 -0
  10. package/bin/run.js +9 -11
  11. package/dist/browser/AuthoritativeSelector.d.ts +3 -2
  12. package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
  13. package/dist/browser/AuthoritativeSelector.js +39 -24
  14. package/dist/browser/AuthoritativeSelector.js.map +1 -1
  15. package/dist/browser/components/platform-fields/MobileFieldControls.d.ts.map +1 -1
  16. package/dist/browser/components/platform-fields/MobileFieldControls.js +2 -2
  17. package/dist/browser/components/platform-fields/MobileFieldControls.js.map +1 -1
  18. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +4 -2
  19. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
  20. package/dist/browser/components/platform-fields/ResourceReferenceField.js +2 -2
  21. package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
  22. package/dist/browser/components/platform-fields/rich-text-value.d.ts.map +1 -1
  23. package/dist/browser/components/platform-fields/rich-text-value.js +11 -1
  24. package/dist/browser/components/platform-fields/rich-text-value.js.map +1 -1
  25. package/dist/browser/components/resource/RecordDetailFrame.js +1 -1
  26. package/dist/browser/components/resource/RecordDetailFrame.js.map +1 -1
  27. package/dist/browser/components/resource/SurfaceFields.d.ts +3 -2
  28. package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
  29. package/dist/browser/components/resource/SurfaceFields.js +14 -7
  30. package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
  31. package/dist/browser/components/resource/resource-import.d.ts +16 -1
  32. package/dist/browser/components/resource/resource-import.d.ts.map +1 -1
  33. package/dist/browser/components/resource/resource-import.js +58 -34
  34. package/dist/browser/components/resource/resource-import.js.map +1 -1
  35. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  36. package/dist/browser/components/todo/ApplicationTodoCenterPage.d.ts.map +1 -1
  37. package/dist/browser/components/todo/ApplicationTodoCenterPage.js +1 -2
  38. package/dist/browser/components/todo/ApplicationTodoCenterPage.js.map +1 -1
  39. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  40. package/dist/browser/components/workflow/StandardWorkflowPages.js +54 -52
  41. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  42. package/dist/browser/platform-client.d.ts +3 -2
  43. package/dist/browser/platform-client.d.ts.map +1 -1
  44. package/dist/browser/platform-client.js +69 -18
  45. package/dist/browser/platform-client.js.map +1 -1
  46. package/dist/browser/record-detail.css +3 -2
  47. package/dist/browser/runtime.d.ts.map +1 -1
  48. package/dist/browser/runtime.js +26 -2
  49. package/dist/browser/runtime.js.map +1 -1
  50. package/dist/browser/workflow-launch.d.ts +4 -1
  51. package/dist/browser/workflow-launch.d.ts.map +1 -1
  52. package/dist/browser/workflow-launch.js +32 -0
  53. package/dist/browser/workflow-launch.js.map +1 -1
  54. package/dist/core.d.ts +1 -1
  55. package/dist/core.d.ts.map +1 -1
  56. package/dist/core.js.map +1 -1
  57. package/documentation/AGENTS.md +26 -0
  58. package/documentation/administration.md +27 -0
  59. package/documentation/application-foundation.md +162 -0
  60. package/documentation/appspec.md +152 -0
  61. package/documentation/backend.md +132 -0
  62. package/documentation/concepts.md +61 -0
  63. package/documentation/data-authz.md +62 -0
  64. package/documentation/delivery.md +110 -0
  65. package/documentation/development.md +32 -0
  66. package/documentation/field-components.md +236 -0
  67. package/documentation/frontend.md +269 -0
  68. package/documentation/getting-started.md +66 -0
  69. package/documentation/interaction-patterns.md +56 -0
  70. package/documentation/manifest.json +120 -0
  71. package/documentation/product-design.md +142 -0
  72. package/documentation/public-access.md +167 -0
  73. package/documentation/reference/cli.md +27 -0
  74. package/documentation/reference/mcp.md +649 -0
  75. package/documentation/testing.md +63 -0
  76. package/documentation/upgrading.md +39 -0
  77. package/documentation/workflow-events.md +181 -0
  78. package/launcher-skill/openxiangda/SKILL.md +24 -0
  79. package/package.json +72 -9
  80. package/releases/2.0.0.json +50 -0
  81. package/skills/manifest.json +2 -2
  82. package/skills/openxiangda-v2/SKILL.md +64 -51
  83. package/skills/openxiangda-v2/agents/openai.yaml +2 -2
  84. package/skills/openxiangda-v2/references/administration.md +27 -0
  85. package/skills/openxiangda-v2/references/application-foundation.md +162 -0
  86. package/skills/openxiangda-v2/references/appspec.md +132 -47
  87. package/skills/openxiangda-v2/references/backend.md +101 -248
  88. package/skills/openxiangda-v2/references/cli.md +27 -0
  89. package/skills/openxiangda-v2/references/concepts.md +61 -0
  90. package/skills/openxiangda-v2/references/data-authz.md +36 -388
  91. package/skills/openxiangda-v2/references/delivery.md +110 -49
  92. package/skills/openxiangda-v2/references/development.md +32 -0
  93. package/skills/openxiangda-v2/references/field-components.md +236 -0
  94. package/skills/openxiangda-v2/references/frontend.md +254 -280
  95. package/skills/openxiangda-v2/references/getting-started.md +66 -0
  96. package/skills/openxiangda-v2/references/interaction-patterns.md +56 -0
  97. package/skills/openxiangda-v2/references/mcp.md +649 -0
  98. package/skills/openxiangda-v2/references/product-design.md +142 -0
  99. package/skills/openxiangda-v2/references/public-access.md +92 -84
  100. package/skills/openxiangda-v2/references/testing.md +45 -56
  101. package/skills/openxiangda-v2/references/upgrading.md +39 -0
  102. package/skills/openxiangda-v2/references/workflow-events.md +143 -285
  103. package/skills/openxiangda-v2/references/architecture.md +0 -9
  104. package/skills/openxiangda-v2/references/commands.md +0 -21
  105. package/skills/openxiangda-v2/references/discovery.md +0 -15
  106. package/skills/openxiangda-v2/references/workspace.md +0 -62
@@ -1,151 +1,81 @@
1
- # Workflow, Events and Notification Hub
2
-
3
- Read [Architecture](architecture.md), [Data and authorization](data-authz.md)
4
- and [Testing](testing.md) before enabling this optional layer.
5
-
6
- Use these optional modules only when the application has a real standard approval or cross-channel notification requirement. Ordinary CRUD and application-specific state machines stay on Data API/App API and must not pull Workflow or Notification code into the default template.
7
-
8
- ## Boundaries
9
-
10
- - The application owns business records and domain invariants.
11
- - Workflow Kernel v2 owns definitions, instances, tasks, participants, commands, transitions and audit facts.
12
- - Application Events v2 owns durable facts and delivery.
13
- - Notification Hub v2 owns templates, rules, logical messages, channel delivery, callbacks and message operations.
14
- - Named Action business notifications use `OpenXiangdaBusinessNotificationService` and the platform-owned `application.informational.standard` template. App Gateway authorizes the current user once; Notification Hub revalidates the action proof, sends as the application principal, and retains initiating user/action audit. Ordinary users never need `app:notification2:send`.
15
- - Native Identity/AuthZ v2 owns the current user and the union of that user's current application roles. Perspective is read-only and never changes workflow participation or action authorization.
16
- - Never import, query, migrate or call 1.x workflow/message code, tables, APIs, queues, templates or callbacks.
17
-
18
- ## Workflow declaration
19
-
20
- Declare workflow topology and bindings once in `openxiangda.config.ts`. Use only approval, condition and end nodes for the standard module. Every definition `inputSchema` is a closed root object (`type: 'object'`, `additionalProperties: false`). Keep business data in Data API/App API and pass the exact `{ resourceCode, id }` `dataRef`, a separate positive `dataRevision`, and bounded schema-valid facts into prepare/start. Undeclared facts and answers fail closed.
21
-
22
- `workflows.activations` is the complete desired set for the deployed version,
23
- not a patch list. Removing a declaration deactivates that workflow for new
24
- starts. Every assignee provider binding uses candidate `min`/`max` defaults of
25
- 1/200 and may not exceed 200. Application Provider callbacks accept only the
26
- signed `openxiangda.workflow-assignee-request/v2.1` envelope, including the
27
- business/data/version tuple and `factDigest`.
28
-
29
- Every definition declares a launch mode: `standalone` for a self-contained
30
- generated durable-process page, `custom-page` for an application operation page,
31
- `hidden-handoff` for a non-menu handoff route, or `work-center-only` when users
32
- only act on existing tasks. `standalone` exposes the standard user launch
33
- route, while `hidden-handoff` keeps the same route out of portal navigation;
34
- the generated browser runtime uses the workflow's localized definition title,
35
- so applications do not maintain code-to-title maps. `standalone` and
36
- `hidden-handoff` default to a compiler-owned
37
- `processOperationCode` and standard process commit. When the subject mutation is
38
- owned by an App Operation, declare `launch.submission.kind: 'named-operation'`
39
- instead. The compiler seals its POST path, capability, schemas, input sources,
40
- result paths and optional context prefill; the standard page invokes that exact
41
- operation and persists only a returned `commandId`. Missing/null
42
- `processCommand` is an explicit successful no-approval result. The browser never
43
- owns a save callback, facts/dataRef, preparation token, or direct start. Custom
44
- pages launch only inside a verified Named Action through
45
- `OpenXiangdaBusinessProcessService`.
1
+ # Workflow 与 Notification Hub 2.0 边界
46
2
 
47
- ```ts
48
- launch: {
49
- mode: 'hidden-handoff',
50
- submission: {
51
- kind: 'named-operation',
52
- create: {
53
- operationCode: 'reservation.submit',
54
- inputs: {
55
- idempotencyKey: { source: 'idempotency-key' },
56
- applicant: { source: 'current-user-reference' },
57
- venue: { source: 'field', fieldCode: 'venue' },
58
- startAt: { source: 'field', fieldCode: 'startAt' },
59
- purpose: { source: 'field', fieldCode: 'purpose' },
60
- },
61
- output: {
62
- subjectId: 'id',
63
- subjectRevision: 'revision',
64
- processCommand: 'processCommand',
65
- },
66
- },
67
- context: [{ queryParameter: 'venueId', fieldCode: 'venue' }],
68
- },
69
- }
70
- ```
3
+ 标准 Workflow 与 Notification Hub 已作为 OpenXiangda 2.0 可选平台模块重新开放。它们不属于默认 CRUD 模板,也不兼容或复用 1.x 工作流、消息中心、模板、卡片、回调、表和 API。
71
4
 
72
- Every required operation request property must have one input binding. Use
73
- `field`, `idempotency-key`, `current-user-reference`, `requested-at`, and—for an
74
- `existing` intent—both `subject-id` and `subject-revision`. Context parameters
75
- only prefill declared fields; the Named Action still derives identity, validates
76
- authorization/business invariants and owns idempotency. Its
77
- `platformAccess.workflow.codes` must include this workflow.
5
+ ## 标准详情与当前用户入口
78
6
 
79
- Use an assignee Provider only when resolution depends on application data. The Provider returns stable Native user/role subjects and never makes the final task authorization decision.
7
+ 普通记录、流程记录、任务和实例复用同一详情框架。流程详情提供申请内容、审批历史和变更记录三个标签页;管理员在当前抽屉或页面中切换到普通表单编辑,直接保存并自动留下变更记录,审批结果保持不变。PC 子表在表格内编辑,父表提交时统一校验。
80
8
 
81
- The standard operations are approve, reject, return, resubmit, transfer, delegate, add-sign, withdraw, admin reassign, admin terminate and reminder. Render only operations returned by the task/instance Surface. A template or application action cannot add a Workflow command that is absent from the Surface.
9
+ 待办中心通过 Workflow 查询 `pending`、`handled`、`created`、`cc` 四种视图;消息中心默认向 Notification Hub 查询 `view=all`,沿用应用声明的渲染扩展。各入口按服务端 `detailNavigation` 打开详情,页面不自行拼接人员范围。
82
10
 
83
- ## Submission and concurrency
11
+ 抄送由动态 Surface 提供 `cc` 操作,使用 `user_select` 多选组件收集 1 至 20 位人员。任务 Surface 可以正式包含实例抄送命令;前端按 `operation.execute.href` 和该 Surface 的 token 提交,不另造任务命令或 token。公共事件为 `openxiangda.workflow.instance.cc_added.v2`。
84
12
 
85
- 1. Commit the bounded Native mutation and Workflow intent as one durable command.
86
- 2. Persist `commandId` in the route and reload `ProcessCommandSurface` after refresh.
87
- 3. Answer only requirements emitted for that command revision.
88
- 4. Let the platform worker start the pinned Workflow exactly once.
89
- 5. Use the Surface revision/concurrency token for every command.
13
+ ## 能力 owner
90
14
 
91
- Duplicate commands return the stored command receipt. Stale revisions fail with a structured conflict. A task/participant state transition, operation log, Workflow fact and event outbox commit atomically.
15
+ - Workflow Kernel v2 唯一拥有定义、实例、任务、参与人、命令、流转、委托、加签和流程审计。
16
+ - Application Events v2 唯一拥有已提交事实的 journal/outbox、顺序、投递和回执。
17
+ - Notification Hub v2 唯一拥有逻辑消息、模板、规则、渠道路由、渲染、发送、更新、关闭、外部绑定、动作收据和消息运维。
18
+ - Data API/App API 唯一拥有业务记录;Workflow 只保存 `dataRef` 和 revision-bound facts,消息只保存允许展示的安全投影。
19
+ - Native Identity/AuthZ v2 唯一拥有当前用户、当前应用角色并集和数据范围;Perspective 只影响读取展示,不改变 Workflow 的发起、任务与操作授权。
92
20
 
93
- ## Notification profile
21
+ Workflow 发起只接受 `{ resourceCode, id }` 形式的 `dataRef`,并要求独立的
22
+ 正整数 `dataRevision`。definition 的 `inputSchema` 根对象必须显式
23
+ `additionalProperties: false`,未声明的 facts/answers 直接拒绝。Provider
24
+ 回调只接受带完整业务/数据/definition/binding/fact digest 的
25
+ `openxiangda.workflow-assignee-request/v2.1`。
94
26
 
95
- A node may reference a notification profile that selects:
27
+ ## 标准工作流范围
96
28
 
97
- - source facts that create, update or close a message;
98
- - audiences such as current participant, initiator, historical participant or CC;
99
- - a versioned template reference;
100
- - an allowlisted variable projection;
101
- - channel priority/fallback;
102
- - default detail target;
103
- - whether to show Workflow Surface actions;
104
- - whether a separate result message is required.
29
+ 首期只支持 `approval`、`condition`、`end`,以及 `single`、`any`、`all`、`sequence` 审批模式。标准操作为提交、同意、拒绝、退回、重新提交、转交、委托、前/后加签、撤回、管理员改派、管理员终止和不改变流程状态的催办。
105
30
 
106
- Template variables must be declared by JSON Schema and stay within the field policy visible to the recipient. The Notification Hub does not query arbitrary application tables to complete a template.
31
+ 复杂业务状态机继续放在应用领域服务。不要把任意 JavaScript、Service Task、BPMN、通用长事务或业务记录复制进 Workflow。
107
32
 
108
- For a non-Workflow informational message, the public business contract accepts
109
- only `title`, optional `summary`, recipients and a canonical navigation target.
110
- Supply stable `eventId`, `idempotencyKey`, `messageKey` and monotonic
111
- `sourceSequence`: duplicate events and idempotency keys replay the stored
112
- message, while a later source sequence converges the same logical message.
113
- Never forward a user token, application credential or global Notification Hub
114
- permission from application code.
33
+ 所有页面和消息动作必须来自后端 Workflow Surface 的 `operations[]`。前端、模板和渠道 Adapter 不自行推断操作权限。
34
+ Surface 同时签发短期、一次性的 `commandToken`,绑定当前用户/会话、应用环境
35
+ Head、实例/任务版本、允许的命令集合与 CSRF。命令请求只提交
36
+ `commandToken + idempotencyKey + input`;旧的 caller-authored
37
+ `expectedTaskVersion`/`expectedInstanceVersion` 不再是合同。冲突必须刷新 Surface,
38
+ 不得自动重放旧意图。
115
39
 
116
- A signed event/date-trigger consumer uses
117
- `OpenXiangdaBusinessNotificationService.sendFromEvent()`. The application
118
- declares safe paths under event `data` for recipients, title and summary;
119
- Notification Hub verifies the active durable delivery and resolves those paths
120
- itself. The audit actor remains the application event consumer with eventId,
121
- deliveryId and subscription code as initiating source. Missing or forged event
122
- proof, arbitrary recipient values and user impersonation are rejected.
40
+ ## 事实和消息投影
123
41
 
124
- The owning subscription must also declare the dependency explicitly so the
125
- immutable package capability closure cannot omit Notification Hub:
42
+ Workflow 在同一 PostgreSQL 事务中提交状态、fact 和 outbox。每个实例使用严格单调的 `instanceSequence`,每位审批人拥有独立 participant 生命周期。
126
43
 
127
- ```ts
128
- {
129
- code: 'proposal-reminder-notification',
130
- eventTypes: ['proposal.reminder.requested.v1'],
131
- platformAccess: {
132
- notification: { mode: 'business-standard' },
133
- },
134
- }
135
- ```
44
+ Native Data 事件的 capture plan 由当前 Head 的 Event、Data、AuthZ revision
45
+ 与 resource 四元组共同确定。新增快照字段或扩展 RLS 会形成新的不可变 plan,
46
+ 不会与旧 plan 冲突;历史 replay/re-emission 复制原 fact 的数据与 plan digest,
47
+ 不会用当前 Head 重新投影历史事实。
136
48
 
137
- Do not add this declaration to subscriptions that do not call
138
- `sendFromEvent()`. Unknown access kinds and notification modes fail closed.
49
+ 每条 Workflow fact v2 都必须携带不可变实例身份信封:`workflowCode`、
50
+ `definitionVersion`、`bindingVersion`、`instanceId`、`generation`、
51
+ `businessKey`、`instanceSequence`、`revision`、`dataRef`、`dataRevision`、
52
+ `actor` 与 `cause`。消费者不得从当前 Workflow Head 反推历史事实版本。
139
53
 
140
- ## Detail navigation
54
+ 配置中的 `workflows.activations` 是完整 desired set;删除声明即停用新发起,
55
+ 不是增量 patch。definition 和 activation 必须一致声明
56
+ `acceptedCommandDeactivationPolicy`:`finish-pinned` 让已接受的 durable process
57
+ command 按固定版本完成,`cancel-on-deactivate` 在声明删除后取消尚未启动的命令;
58
+ 既有 Workflow instance 始终按固定版本继续。所有审批人 Provider 的 `min/max`
59
+ 默认 1/200,最大 200。
60
+ 需要按流程实例串行投递时只声明 `ordering: 'workflow-instance'`,不接受下划线别名。
141
61
 
142
- Use a canonical navigation target with `PLATFORM_ROUTE`, `APP_ROUTE` or allowlisted `EXTERNAL_URL`, stable route code, path/query parameters and an authenticated access requirement. Do not place environment domains, bearer tokens, cookies, identity-switching credentials or secrets in template strings.
62
+ Notification Hub 消费事实:
143
63
 
144
- The Notification Hub resolves desktop, mobile and channel deep links. The landing page always re-runs platform authentication and Data/AuthZ/Workflow authorization.
64
+ - `participant.activated` 创建待处理消息;
65
+ - participant 完成、转交、委托、加签、暂停或恢复时更新对应收件人的逻辑消息;
66
+ - instance 完成、拒绝、撤回或终止时更新该实例全部历史消息;
67
+ - 消息回调必须调用 Workflow 命令,等待 Workflow 新事实后再更新卡片。
145
68
 
146
- Business-record navigation and custom Workflow detail are separate contracts.
147
- To let the standard Workflow page offer “view business detail”, declare an
148
- explicit route pair on the subject resource:
69
+ 发送采用至少一次语义,以 `messageKey + recipient + channel` 幂等。RabbitMQ 只负责唤醒,PostgreSQL fact、message projection、outbox 和 receipt 才是恢复事实。
70
+
71
+ ## 详情跳转
72
+
73
+ 消息使用 canonical `navigationTarget`,而不是模板拼接环境 URL。它可以描述平台路由、应用路由或白名单外部地址,由平台按 tenant、environment 和 channel 解析 desktop/mobile/deep link。
74
+
75
+ 详情地址不能携带登录 Token、Cookie 或任何身份替换凭据。点击后必须重新认证,并由 Data/AuthZ/Workflow 执行真实权限校验;当前 Perspective 只能重新应用读取投影。
76
+
77
+ “跳回业务记录”和“完全接管流程详情”是两个不同合同。标准 Workflow 页需要提供
78
+ “查看业务详情”时,在 subject resource 上声明明确的 route code 对:
149
79
 
150
80
  ```ts
151
81
  const purchaseResource = {
@@ -159,165 +89,93 @@ const purchaseResource = {
159
89
  } satisfies AppDataResourceDeclaration;
160
90
  ```
161
91
 
162
- Both routes are authenticated `surface: 'user'` routes, require
163
- `app:<app-code>:data:<resource-code>:read`, and contain exactly one dynamic
164
- record parameter. Desktop stays outside `/m`; mobile starts with `/m/`. The
165
- parameter may be named for the domain (`:purchaseId`, `:applicationId`, and so
166
- on). The compiler seals the mapping and the platform renders it from the same
167
- active Contract revision used for Workflow navigation. With no mapping the
168
- Surface returns no business-record target. It never guesses a path from the
169
- resource code, and a malformed active mapping fails closed.
92
+ 两条 route 都必须是已认证的 `surface: 'user'` 页面并要求该资源的 read capability;
93
+ 桌面 path 不得进入 `/m`,移动 path 必须以 `/m/` 开头,且各自只能包含一个动态记录参数。
94
+ 参数名可以是业务语义名,例如 `:purchaseId` 或 `:applicationId`。编译器把映射写入
95
+ 不可变资源 Contract 和生成的 `resourceDefinitions`,平台从同一环境 active Contract
96
+ 读取并代入 Workflow Surface 已授权的 recordId。未声明时返回空业务记录目标;已发布
97
+ 映射畸形时严格失败。平台和浏览器都不得再按 `resourceCode` 猜路径,也不得用别名补洞。
170
98
 
171
- To replace the standard Workflow detail page everywhere, declare both route
172
- codes on the Workflow definition declaration. The desktop route must be an
173
- `admin` route, the mobile route must be a `user` route, and both paths contain
174
- exactly `:instanceId`:
99
+ 流程需要完全接管详情页时,在 definition declaration 上声明桌面端与移动端 route code:
175
100
 
176
101
  ```ts
177
- frontend: {
178
- root: 'apps/web',
179
- routes: [
180
- {
181
- code: 'purchase-detail',
182
- path: '/admin/operations/purchases/:instanceId',
183
- label: '采购审批详情',
184
- surface: 'admin',
185
- },
186
- {
187
- code: 'purchase-detail-mobile',
188
- path: '/m/purchases/:instanceId',
189
- label: '移动采购审批详情',
190
- surface: 'user',
191
- },
192
- ],
193
- },
194
- workflows: {
195
- definitions: [{
196
- version: 1,
197
- definition: purchaseApproval,
198
- launch: { mode: 'work-center-only' },
199
- detailRouteCode: {
200
- desktop: 'purchase-detail',
201
- mobile: 'purchase-detail-mobile',
202
- },
203
- }],
204
- bindings: [],
205
- activations: [],
206
- },
102
+ {
103
+ version: 1,
104
+ definition: purchaseApproval,
105
+ launch: { mode: 'work-center-only' },
106
+ detailRouteCode: {
107
+ desktop: 'purchase-detail',
108
+ mobile: 'purchase-detail-mobile',
109
+ },
110
+ }
207
111
  ```
208
112
 
209
- Bind the generated routes with `defineApplicationContributions`. Inside the
210
- page, read `instanceId` from React Router and optional `taskId` from the query.
211
- Use `loadWorkflowInstanceSurface`, `loadWorkflowTaskSurface` and
212
- `loadWorkflowTimeline` from `openxiangda/core`; render only operations returned
213
- by the Surface. The platform resolves the same declaration for desktop/mobile
214
- work centers, Notification Hub logical messages, DingTalk cards and other
215
- channel snapshots. Omitting `detailRouteCode` keeps the standard pages. A
216
- declared but missing or incompatible route fails closed rather than falling
217
- back silently.
218
-
219
- When `detailRouteCode` is omitted, use the platform standard detail page. Its
220
- Surface carries `presentation.businessDetail`, pinned to the instance Data
221
- logical revision, declaration digest and physical resource while re-reading the
222
- current values of the same Native Data record on every request. The start-time
223
- `requestedRevision` remains CAS metadata; `sourceRevision` and visible values
224
- must advance after a business-record update and must never become start-time
225
- value snapshots. The projection applies the current user's field policy and
226
- removes every `system: true` or internal field plus any empty group. Parent and subtable
227
- resources stay on that same logical revision and each subtable is bounded to
228
- 200 rows. Render the provided summary, full field Surface, typed timeline and
229
- server-owned operation descriptors; do not infer labels, operation hierarchy
230
- or field semantics from codes in application code. Desktop and mobile have
231
- independent renderers, low-frequency operations live under one more-actions
232
- entry, and comments are collected only after an operation is selected. A
233
- `stale` detail status is metadata, not a persistent banner or an action block;
234
- show the friendly refresh prompt only when a command returns a real
235
- `freshSurface` token/CAS conflict and never auto-replay it. Merge operation
236
- actor/action/reason/time into the corresponding vertical node instead of
237
- rendering a second audit block. Never render UUIDs, revision/event diagnostics
238
- or a technical information section. Resolve the applicant from a directory or
239
- stable snapshot and use a friendly placeholder instead of exposing userId.
240
-
241
- The standard desktop task and instance pages use `/tasks/:taskId` and
242
- `/workflows/:instanceId` as standalone full-screen routes outside the admin
243
- Shell. Historical `/admin/tasks/:taskId` and `/admin/workflows/:instanceId`
244
- routes have no alias or redirect. Mobile remains the
245
- independent `/m/tasks/:taskId` and `/m/workflows/:instanceId` surface.
246
-
247
- Ordinary records and Workflow task, instance and record entrances share the
248
- standard detail frame: gray background, grouped white cards, title/status/creator
249
- metadata and fixed actions. Workflow adds application content, approval history
250
- and change-history tabs. An authorized administrator edits the full ordinary
251
- form inside that same drawer or page and saves directly; do not introduce an
252
- edit-application dialog, correction reason or extra confirmation. Native change
253
- history records the edit while Workflow approval facts remain unchanged.
254
- Desktop child rows edit inline and parent submission validates every row.
255
-
256
- The standard work center reads the server's `pending`, `handled`, `created` and
257
- `cc` views and counts. The separate message center reads Notification Hub with
258
- `view=all` by default and preserves application renderer extensions. Follow each
259
- item's `detailNavigation`; do not rebuild user eligibility from client data.
260
- Dynamic `cc` operations use `user_select` with `mode: 'multiple'` and a bounded
261
- `userIds` array. Execute the exact Surface href with its issued command token:
262
- a task Surface may officially include an instance CC command. The public fact
263
- is `openxiangda.workflow.instance.cc_added.v2`.
264
-
265
-
266
- Files, rich-text images and signatures on this page use only the Workflow
267
- instance-scoped preview/content client. Every request is rebound to the current
268
- principal, authorized instance, projected resource/record/field and managed
269
- file id. Never substitute the ordinary Data API file route or put credentials
270
- in a file URL.
271
-
272
- Standard launch entry is also a platform user Surface. Desktop uses
273
- `/workflows/:workflowCode/start` and mobile uses
274
- `/m/workflows/:workflowCode/start`; both first load
275
- `loadWorkflowLaunchSurface(workflowCode)`, verify its compiler-owned subject and
276
- submission contract, then use the standard durable process commit or the exact
277
- sealed Named Action.
278
- `custom-page` and `work-center-only` declarations do not expose the standard
279
- launch Surface. After start, navigate through the device's standard instance
280
- entry so the current `detailRouteCode` contract can redirect consistently.
281
-
282
- An application-level inbox is enabled with
283
- `frontend.user.applicationTodoCenter: true`. It projects Notification Hub messages and
284
- per-recipient interaction for the current logged-in user only; it is not the
285
- Notification Hub management console. Use its `去处理` navigation to enter the
286
- authorized Workflow or application page, and keep approve/reject/transfer
287
- commands on the destination Surface.
288
-
289
- ## Interactive channel actions
290
-
291
- Channel callbacks enter the Notification Action Gateway. The gateway verifies tenant, environment, recipient identity, task/participant, Surface revision, operation, nonce, signature and expiry before calling Workflow.
292
-
293
- The callback never writes Workflow or business state directly. It returns the Workflow command receipt, waits for the resulting fact and lets Notification Hub update the existing logical message. Duplicate or expired clicks return the current receipt/snapshot and refresh the card.
294
-
295
- Use direct card actions first for approve/reject. Return, transfer, delegate and add-sign normally deep-link to the standard task page because they require structured input.
296
-
297
- ## Event handling
298
-
299
- Consume typed participant/task/instance facts with durable idempotency. Declare
300
- event delivery `ordering: 'workflow-instance'` when the handler requires the
301
- platform to serialize by instance; `workflow_instance` is not an alias. Still
302
- verify `instanceSequence` in the consumer and never assume global event order.
303
- Terminal instance facts dominate stale participant facts and update every
304
- historical message for that instance.
305
-
306
- For Native Data facts, the platform owns the capture plan under the exact Event
307
- revision, Data revision, AuthZ revision and resource tuple selected by the
308
- Native Head. Adding a snapshot field or extending RLS may create a new plan;
309
- applications do not version it and must not remove the field to satisfy an old
310
- plan. Historical replay uses the data and plan digest frozen in the original
311
- fact, so it never gains fields from the current Head.
312
-
313
- Every Workflow fact v2 carries the immutable instance identity envelope:
314
- `workflowCode`, `definitionVersion`, `bindingVersion`, `instanceId`,
315
- `generation`, `businessKey`, `instanceSequence`, `revision`, `dataRef`, and
316
- `dataRevision`, plus `actor` and `cause`. Treat missing identity as an invalid
317
- fact; never infer historical versions from the current Workflow Head.
318
-
319
- Use Fake Channel before a real Adapter. Verify create, update, close, reconcile, duplicate, out-of-order, restart, retryable failure, terminal failure and unknown delivery before DingTalk or campus canaries.
320
-
321
- ## Verification
322
-
323
- Run the workspace generate/check/test/build gates plus real current-user positive and negative role-union cases. Acceptance must include full business-detail projection, bounded parent/subtable reads, Workflow-scoped files, independent desktop/mobile renderers, submit, approve, reject, return/resubmit, transfer, delegate, before/after add-sign, withdraw, terminate, duplicate commands, stale revisions, repeated callbacks, full terminal card updates, detail navigation and 1.x zero-touch evidence.
113
+ `desktop` 必须引用 `surface: 'admin'` 的 route,`mobile` 必须引用
114
+ `surface: 'user'` 的 route;两个 path 都必须且只能包含
115
+ `:instanceId`。任务入口由平台追加有界 `taskId` query。应用使用
116
+ `defineApplicationContributions(appRoutes, { pages })` 绑定桌面和移动组件,
117
+ 页面通过 `openxiangda/core` 的 `loadWorkflowInstanceDetail`、
118
+ `loadWorkflowTaskDetail` 读取修订一致的聚合详情,只渲染 Surface 返回的操作。
119
+ 旧 Surface 与 timeline 读取继续供兼容客户端使用。
120
+
121
+ 平台在当前环境 Head 的不可变 Contract Bundle 上统一解析:桌面/移动工作台、
122
+ Notification Hub 的 `workflow.detail` 逻辑目标、钉钉卡片以及其他渠道快照得到
123
+ 同一组自定义路径。未声明时保留标准详情;声明存在但 route 缺失、surface 错误或
124
+ path 不合法时严格失败,不退回标准页掩盖合同错误。
125
+
126
+ 标准详情由平台统一渲染,不要求应用复制资源详情页。Workflow Surface 的
127
+ `presentation.businessDetail` 按实例固定的 Data logical revision、资源声明 digest 和
128
+ physical resource 定位结构,但每次读取同一 Native Data record 的当前字段值;发起时
129
+ `requestedRevision` 只用于命令 token/CAS,当前 `sourceRevision` 和当前值始终随重新读取更新,
130
+ 禁止把业务字段值改成流程启动快照。字段投影同时应用当前用户策略并统一排除
131
+ `system: true`、平台内部字段与过滤后为空的分组;
132
+ 父子表按同一 logical revision 和父记录外键读取,单表最多 200 行。页面同时消费
133
+ `presentation.summary`、服务端 timeline `display.entries` 和 operation descriptors。
134
+ 桌面端与移动端拥有独立 DOM;同意/拒绝等决策操作固定在底部主操作区,转交、退回、
135
+ 加签等低频操作进入“更多操作”,意见只在点击动作后的确认弹窗或移动端底部抽屉中输入。
136
+ `stale` 只是 revision 元数据,不显示常驻横幅也不预先阻断操作;只有服务端命令 token/CAS
137
+ 返回真实 `freshSurface` 冲突时才提示“内容已更新,请刷新后重试”,且绝不自动重放命令。
138
+ 同意、拒绝、退回、转交和加签记录并入对应纵向节点;实例撤回/终止是独立终态事件,
139
+ 展示操作者、动作、意见和唯一 `primaryDisplayTime`,
140
+ 不另设“操作记录”。标准业务页不渲染流程/任务/资源/记录 UUID、事件序列、revision 或失败指针。
141
+ 申请人、审批人和操作者显示目录/快照解析名称、可选部门以及真实头像;缺失或加载失败时
142
+ 统一使用平台默认头像,不生成姓名首字,也不回退 userId。
143
+
144
+ PC 标准任务与实例详情的 canonical 路径分别为 `/tasks/:taskId` 和
145
+ `/workflows/:instanceId`,使用独立全屏页面,不进入后台 Shell;不提供旧 admin 路径的
146
+ 别名或重定向。移动端继续使用独立的 `/m/tasks/:taskId` 和 `/m/workflows/:instanceId` 页面。
147
+
148
+ 流程详情中的附件、富文本图片和签名不直接复用普通 Data API 文件地址。浏览器调用
149
+ Workflow instance-scoped preview/content 路由,平台在每次文件读取时重新校验当前身份、
150
+ 实例参与权限、固定业务记录、资源、字段和 file id;URL 不携带登录态或替代身份。
151
+
152
+ 标准发起页使用独立的 `WorkflowLaunchSurface` 读取当前激活合同:桌面路径为
153
+ `/workflows/:workflowCode/start`,移动路径为
154
+ `/m/workflows/:workflowCode/start`。`standalone`/`hidden-handoff` 缺省使用同一个
155
+ compiler-owned `processOperationCode`、subject declaration 和标准 process commit;平台在一个
156
+ 事务中写业务数据和 durable command。action-owned 资源改为声明
157
+ `launch.submission.kind: 'named-operation'`,显式绑定 create/existing 请求来源、响应 subject/
158
+ processCommand 路径和 allowlisted context 预填。标准页调用原 Named Action,由应用服务端保留
159
+ 业务校验、幂等与原子提交;缺少/null processCommand 是成功的免审结果,不生成流程。页面只在
160
+ 确实返回 command 时把 `commandId` 写入 URL,刷新、跨设备和 worker 恢复都重新读取
161
+ `ProcessCommandSurface`;`awaiting_input` 只回答已生成 requirement,`started` 再进入 pinned
162
+ instance entry。浏览器不再导出 prepare/start 发起协议。
163
+
164
+ 通用应用待办页通过 `frontend.user.applicationTodoCenter: true` 启用,平台同时提供
165
+ `/todos` 和 `/m/todos`。它只投影当前登录用户的 Notification Hub 收件人数据,
166
+ `查看详情` 使用上述统一导航解析;待办页不常驻业务详情,审批命令仍在目标 Workflow
167
+ Surface 上执行。
168
+
169
+ ## 应用声明
170
+
171
+ 应用只在明确启用可选模块时,在 `openxiangda.config.ts` 声明 workflow definition、binding、assignee provider、notification profile、template reference 和安全字段投影。Git 声明是拓扑事实源;平台管理页面只负责版本、激活、预览、诊断和有界运行参数,不建立第二份活动定义。
172
+
173
+ 事件处理器调用 `OpenXiangdaBusinessNotificationService.sendFromEvent()` 时,
174
+ 对应订阅还必须声明
175
+ `platformAccess: { notification: { mode: 'business-standard' } }`。编译器将该声明写入
176
+ 不可变 Configuration/Contract Bundle,并据此生成精确的 `notification-hub-v2`
177
+ 能力闭包;不发送通知的订阅不得声明该依赖,未知 access 或 mode 直接失败。
178
+
179
+ ## 验证
180
+
181
+ 至少验证:完整标准详情投影、父子表边界、流程附件读取、桌面/移动独立渲染、全部标准操作、多人模式、重复/并发命令、stale revision、重启 replay、乱序事件、重复回调、消息终态全量更新、详情跳转、错用户/错租户/过期动作、渠道超时/unknown、死信重放和 1.x 零触碰。
@@ -1,9 +0,0 @@
1
- # OpenXiangda 2.0 Architecture
2
-
3
- Keep one React application, one optional NestJS service and one platform-owned data contract. Application code directly depends only on `openxiangda`; use `openxiangda/config`, `openxiangda/core`, `openxiangda/field-kit`, `openxiangda/react`, `openxiangda/nest` and `openxiangda/testing` as the public subpaths. Do not import the physical `openxiangda-*` implementation packages.
4
-
5
- Declare resources, routes, capabilities, row policies and operation-aware field policies once in `openxiangda.config.ts`. The compiler owns derived contracts; the root package owns Field Kit, CRUD Renderer, Shell, clients and Nest integration; the platform owns identity, authorization, data, environment Heads and deployment state.
6
-
7
- The compiler alone derives the AppPackage `requiredPlatformCapabilities` closure. Each entry binds a capability `code`, exact `contractVersion` and deterministic `usageDigest` of only that capability's normalized declarations. Never write `platform.requiredCapabilities`, append requirements through a build API, edit the AppPackage or accept a feature whose status is not `available` or whose contract version differs. The platform authoritatively recomputes the closure from the sealed config and contract artifacts.
8
-
9
- Ordinary list, detail, create, update and delete operations go directly through Data API. Add a NestJS endpoint only for a business action that cannot be represented as CRUD. Run `pnpm openxiangda check` after every contract change.
@@ -1,21 +0,0 @@
1
- # OpenXiangda 2.0 Commands
2
-
3
- > Generated from `DEVKIT_COMMANDS`. Do not edit manually.
4
-
5
- | Command | Risk | Purpose |
6
- | --- | --- | --- |
7
- | `pnpm openxiangda create` | deploy | 创建、绑定并初始化应用 |
8
- | `pnpm openxiangda dev` | write-local | 连接平台测试数据启动本地 Web,按需启动 Nest |
9
- | `pnpm openxiangda check` | write-local | 生成契约并在目标平台预检后执行检查、测试和构建 |
10
- | `pnpm openxiangda accept` | deploy | 按计划准备可选的真实预发验收身份 |
11
- | `pnpm openxiangda deploy` | deploy | 部署测试环境或显式复用测试版本部署生产 |
12
- | `pnpm openxiangda status` | read | 查询最近或指定部署状态 |
13
- | `pnpm openxiangda logs` | read | 查询最近或指定部署日志 |
14
- | `pnpm openxiangda cancel` | deploy | 幂等取消尚未提交激活的部署 |
15
- | `pnpm openxiangda retry` | deploy | 显式重试可恢复的失败部署 |
16
- | `pnpm openxiangda start` | deploy | 从当前不可变版本启动应用环境 |
17
- | `pnpm openxiangda stop` | deploy | 将应用环境缩容为零并保留数据 |
18
- | `pnpm openxiangda rollback` | deploy | 回滚测试或生产环境 |
19
- | `pnpm openxiangda login` | write-local | 通过平台浏览器授权登录 |
20
- | `pnpm openxiangda skill` | write-local | 安装当前版本的 AI Skill |
21
- | `pnpm openxiangda spec` | write-local | 维护可选的 AppSpec 需求与变更辅助 |
@@ -1,15 +0,0 @@
1
- # Requirement Discovery
2
-
3
- Before editing, write a compact decision record covering:
4
-
5
- - business objective and measurable outcome;
6
- - actors and positive/negative scenarios;
7
- - resources, field meanings, required values and lifecycle states;
8
- - role memberships, operation capabilities, field restrictions and row scope;
9
- - external systems, side effects, concurrency and idempotency;
10
- - environment, security, volume and latency bounds;
11
- - acceptance evidence for UI, API, PostgreSQL/RLS and delivery.
12
-
13
- Separate known facts, assumptions and unresolved choices. Inspect the workspace protocol and existing declarations before inventing a contract. If a choice changes data meaning, authorization, external writes or release scope, resolve it before implementation. Ordinary naming or layout details may use a documented reasonable assumption.
14
-
15
- The output is complete only when every requirement maps to an owner and a falsifiable acceptance check. Do not begin with source patches and reconstruct intent afterward.
@@ -1,62 +0,0 @@
1
- # OpenXiangda 2.0 Application Agent Contract
2
-
3
- - Use only the workspace-pinned CLI: `pnpm openxiangda <command>`. Never invoke a bare global `openxiangda` inside a 2.0 task.
4
- - Maintain business goals, task pages, permission decisions and acceptance examples in the existing `appspec/app.md`; complex modules may have focused capability records. `appspec/` is the OpenXiangda 2.0 business-intent layer. Before changing observable behavior, read the bounded index with `pnpm openxiangda spec context --json`, then select only the relevant stable ID; use no ChangeSpec for L0 work, one short ChangeSpec for L1, and add permission/rollback/concurrency detail only for L2/L3. Before close, the user—not AI—confirms `currentSpec=merged` or `not-applicable`. AppSpec is advisory and never a release gate. Never read, import or migrate 1.x `openspec/` or SDD.
5
- - Use Vite, React Router, Refine Core and platform Field Kit. Use Ant Design for PC and the scoped openxiangda/mobile adapter for touch controls; wrap custom mobile pages in MobileSurface and import openxiangda/mobile/styles.css. Do not import the antd-mobile root with its global reset. Do not author raw business input/select/textarea/contentEditable elements. Keep mobile entrypoints under src/mobile/ or name them Mobile*.tsx / *.mobile.tsx so openxiangda check can inspect their local dependency graph. Do not add Umi, ProComponents or another admin shell.
6
- - Use Ant Design's default component appearance on PC and the platform's scoped mobile adapter for touch controls. The UI provider supplies Chinese locale and contextual feedback only. Do not generate appearance preferences, color configuration, palette registries, visual state stores or document-wide color effects. Prefer platform components and keep CSS scoped to the component.
7
- - The template includes Tailwind CSS v4 utilities for user pages. The application owns its minimal document reset; omit preflight from the shared runtime and do not scaffold custom palettes.
8
- - Bind every generated `appRoutes` entry to its local page with `defineApplicationContributions`; desktop `admin` routes stay inside the platform Shell, while `user` routes render without an admin Shell for independent mobile/user experiences. Generated resource CRUD routes are compiler-owned under `/admin/resources/<resourceCode>...` and `/m/admin/resources/<resourceCode>...`; never recreate root resource paths, aliases or redirects. Explicit routes that have the same canonical shape as another explicit or generated route fail compilation, even when dynamic parameter names differ. Use only the typed `toolbar`, `row` and `detail` resource slots for generated resource actions. Declare the complete editable admin menu with `defineAdminNavigation` and its page/group helpers; the Shell renders only generated `adminNavigation` references and permissions only filter them. Do not create another router, menu store, layout, identity provider, permission store or copied CRUD page; route/action access uses capability or `allOf`/`anyOf`, while Data/App API and Workflow authorization remain server-owned.
9
- - Application login is optional `frontend.authentication`: existing platform users only, registration rejected, exact desktop `/login` and mobile `/m/login`. Bind generated `authenticationSurfaces` to separate PC/mobile renderers through `defineApplicationContributions`; renderers own only brand visuals and call `ApplicationLoginSurfaceProps`. The platform alone owns passwords, providers, OAuth state/callbacks, secure cookies, current identity and authorization. Never put login in protected `appRoutes`, call a v1 auth route, store tokens, create users/roles, or add another Router/identity provider. Keep QA in generated `platformAuthManifest`, outside the protected route denominator.
10
- - Branded standard user pages use the optional exact `standardUserSurfaces` contribution from `openxiangda/react`: provide separate desktop/mobile `frame` and `applicationTodoCenter` renderers or omit the property entirely. The platform still owns `/todos`, `/m/todos`, Workflow routes, `RuntimeBoundary`, current-user Notification Hub data, query/load/interaction callbacks and navigation. Never add a second Router, Todo API client, identity store or token prop.
11
- - External users without platform accounts use only an exact static `surface: 'user'` route declared through `frontend.publicAccess`. Declare the one resource, bounded field sets, required operations, draft limits and named duplicate validations; consume only generated `anonymousPublicAccess` and `createAnonymousPublicClient`. `own.list`/`own.read` mean records submitted by the same platform-issued HttpOnly browser credential, not a verified natural person, and another browser or cleared cookie intentionally loses access. Never create a guest role/user, call the general Native Data API, expose an anonymous upload path, store identity locally or derive ownership from IP, user-agent or fingerprint.
12
- - Define storage with `defineDataModel` in `modules/<business>/models.ts`; compose modules with `defineApplicationModule`. Select standard pages explicitly through `crud`, `defineResourceList` and `defineResourceForm`. Models without `crud` receive no pages or navigation. A business task page may consume several models. The root `openxiangda.config.ts` composes modules, routes and roles; it must not grow into one giant business definition. Existing `data.resources` remains a lower-level input to the same compiler. Keep internal fields `hidden: true`; this is presentation, never authorization. `system: true` marks server-owned values and hides them by default; `hidden: false` may show a business status or serial number. Form/detail/list field selections are exhaustive and ordered. A Workflow subject resource may declare explicit desktop/mobile `detailRouteCode` as documented in the workflow contract.
13
- - Never write resource-level `schemaVersion`, `appCode`, `schema`, `surface`, `capabilities`, `fieldPolicies` or `platform/data` modules. The compiler derives the strict DataResource, CRUD capabilities, Surface and AI Schema. The application manifest still starts with its one top-level `schemaVersion: 3`.
14
- - Ordinary list/get/create/update/delete, filters, export and batch operations use the platform Native Data API. Do not create Function CRUD or NestJS wrappers.
15
- - Standard PC lists use one toolbar, on-demand filters/column settings and drawer create/edit over the retained list. Full-page entry uses the same platform form. Declare shared form/detail groups with `crud[].sections: [{ title, fields }]`; each view's field selection remains exhaustive and ordered. Small forms may omit groups. Keep only business columns by default, use lightweight sections and let platform components handle pending, errors and dirty cancellation. Do not recreate list/form layouts in application code.
16
- - Add NestJS only for a named business action that needs a cross-resource transaction, an invariant or an external side effect. The blank app contains no apps/server source or Nest dependency importer. Declare backend.enabled or a server operation, then run pnpm openxiangda check or pnpm dev to initialize and install the optional backend once; existing source is never regenerated. Standard platform workflow definitions and activations do not require Nest.
17
- - One model can declare up to 20 named standard views with `crud[].code` and `name`. Each selects its own list/form/detail fields, sections, generated operations and mobile availability. Bind navigation with `adminResourcePage(modelCode, { viewCode })`; only an unnamed CRUD declaration generates the original default routes. Views share model values, access and Data API ownership. Include writable required fields in create forms or set `generated.create: false`. Keep platform display preferences and authenticated drafts scoped to the selected view; do not copy models or invent a second draft store.
18
- - Every interactive business action binds `@OpenXiangdaOperation(operation)` and injects `OpenXiangdaBusinessDataApiService`, `OpenXiangdaBusinessNotificationService`, or `OpenXiangdaStandardOperations`. The platform checks the action capability once at App API ingress; the trusted backend then has full Data/managed business-notification access only to its exact application/environment while audit retains the initiating user and action. Do not author `authorizationJSON`, forward user tokens, grant ordinary users `app:notification2:send`, or reapply the user's resource, row and field permissions inside the action.
19
- - A NestJS backend declares only `enabled`, `isolation: 'shared' | 'dedicated'` and `resourceProfile: 'light' | 'standard'`. Never put raw Kubernetes resources, replicas, ports or environment maps in application metadata; the platform owns capacity and scaling.
20
- - Use the current logged-in user and the union of that user's application roles. An app for every logged-in platform user may declare one existing package role through `authz.authenticatedUserRoleCode`; the platform materializes it on first access and unions it with manual and business-projected roles. Do not fabricate a browser fallback role or use a business membership resource for this universal audience. An optional declared Perspective projects only read visibility (pages, rows and fields); create/update/delete, workflows and custom actions continue to authorize against the complete union. Standard Data API calls inherit `X-OpenXiangda-Perspective` automatically. Use `@CurrentPerspective()` only when custom Nest code reads outside the standard Data API, and apply an equivalent read projection explicitly. Business code must not persist a platform Token or authorization result and must not implement a second identity path.
21
- - Fields inherit the resource read/create/update capabilities. Use field `access` only to tighten them; arrays are all-of and `false` is explicit deny. There is no `write` fallback.
22
- - Field `type` is semantic, never a hand-authored database type. The supported catalog is exactly `text.short`, `text.long`, `text.rich`, `number.integer`, `number.decimal`, `boolean`, `date`, `time`, `datetime`, `date-range`, `datetime-range`, `option.single`, `option.multiple`, `cascade.single`, `cascade.multiple`, `user.single`, `user.multiple`, `department.single`, `department.multiple`, `resource-ref.single`, `resource-ref.multiple`, `file`, `image`, `signature`, `address`, `location`, `json`, `serial-number`, `uuid` and `subtable`. The compiler alone derives PostgreSQL columns, constraints and indexes.
23
- - Every `date-range` and `datetime-range` field explicitly declares `rangeBoundary: 'closed' | 'half-open'`. Values remain `{ start, end }`; callers never send or override the boundary. Closed ranges allow `start <= end` and use PostgreSQL `[]`; half-open ranges require `start < end` and use `[)`.
24
- - Declare every custom operation capability in `authz.capabilities` with `kind: 'backend'`, then reference that same code from the operation and its allowed roles. Generated resource CRUD capabilities do not go in this catalog.
25
- - Build App Operation request/response schemas with `resourceRecordSchema`, `schemaRef`, `composeJsonSchema` and `composeAppOperationSchemas` from `openxiangda/config`. They project the same Resource declaration and canonical field value protocols; never import a physical toolchain package or repeat user, department, resource-reference or managed-file schemas by hand.
26
- - Visitor duplicate protection uses `createVisitorReservation({ duplicateMatch: { fieldCode: submittedValue }, ... })`. `duplicateMatch` is a non-empty value map, never a field-name array, and no mutable pre-read is allowed.
27
- - Desktop and mobile pages share values, validation and authorization, but use separate renderers. Options, members and departments store display snapshots; resource references store direct JSON display values; attachments, images and signatures use platform-managed file references.
28
- - `option.*`, `user.*`, `department.*` and `resource-ref.*` values never collapse to scalar IDs. Single values store one labeled object and multiple values store object arrays. `resource-ref.*` JSON is not a foreign key, trusted target snapshot or automatically refreshed copy; current target state is read by `resourceCode` plus `value`. A resource source `labelField` must be `text.short` or `text.long`; `serial-number` is allowed in source search, description and snapshot fields, but not as the label. `location` accepts only exact WGS84 coordinates captured by DingTalk or browser geolocation; it has no manual input or `manual` source. Roles that consume directory-backed fields explicitly include `app:<app-code>:directory:read`.
29
- - Use `resourceRoleCapabilities(appCode, resourceCode, "read" | "manage" | [operations])` for explicit role grants. A read grant never expands into create/update/delete when a page is added. Confirm the business permission matrix before implementing multi-role behavior; `manage` is only for roles explicitly allowed all CRUD operations. Use `currentUserDataPolicy(...)` for current-user row scope.
30
- - Do not add compatibility aliases, migration branches or silent fallbacks for an earlier 2.0 alpha contract. Replace an incorrect contract and regenerate the application.
31
- - Standard Workflow and Notification Hub are optional 2.0 modules. Enable them only through canonical `openxiangda.config.ts` declarations and generated clients. `standalone`/`hidden-handoff` default to the compiler-owned process operation; action-owned submission must instead declare `launch.submission.kind: 'named-operation'` with sealed create/existing input and output bindings so the standard PC/mobile page calls the original Named Action and accepts an explicit no-Workflow result. Never add a browser save callback or call Workflow prepare/start directly. Custom pages launch only through a verified Named Action using `OpenXiangdaBusinessProcessService`. PC and mobile render the same `ProcessCommandSurface` independently and recover only by `commandId`. To replace Workflow detail, declare both desktop and mobile `detailRouteCode` values whose routes contain exactly `:instanceId`. Add the current-user todo page only with `frontend.user.applicationTodoCenter: true`; never query Notification Hub management APIs from a user page.
32
- - Run `pnpm openxiangda check --json` after contract changes and read `data.sealedArtifact`; check never seals, and an older `.openxiangda/build/app-package.json` is not the current check result. Use `pnpm openxiangda accept --plan <file>` only for optional real preproduction identity acceptance; it never blocks delivery. Deploy with `pnpm openxiangda deploy`, inspect with `pnpm openxiangda status` and `pnpm openxiangda logs`, and use the platform rollback command rather than mutating K3s directly.
33
- - AI-native clients start the workspace protocol through the same pinned binary: `pnpm exec openxiangda --mcp-stdio --cwd <workspace>`. Read `openxiangda://workspace/contracts` or call `contract_describe`; for first-time menu authoring, copy `data.adminNavigationAuthoring.suggestion.expression` once into `frontend.admin.navigation` with its listed `openxiangda/config` imports, then edit that application-owned declaration. Never treat the proposal as runtime discovery. When AppSpec is enabled, also read `openxiangda://workspace/appspec` or call `appspec_context`. Require `aiCatalog` and `aiCatalogDigest` to match the normal compiler output. This is a stdio transport mode, not another application-owned server. Never write an application MCP server, Catalog file, preview store or second authorization path.
34
-
35
- - For an ordinary CRUD module, omit unused backend/platform blocks; the platform owns CRUD. `pnpm dev` uses connected development and starts Nest only when enabled or required by declared server capabilities. Run interface tests for changed business rules and browser tests for the actual PC/mobile create/edit/save/read journeys; passing HTTP tests alone is not acceptance.
36
-
37
-
38
- ## Standard data entry and list controls
39
-
40
- Use the platform standard list for nested AND/OR conditions, display-column
41
- selection/order/freezing and multiple sorting. Set `requiresSelection: true` on
42
- selection-dependent toolbar contributions. Preserve hidden fields and query /
43
- export parity. Column changes preview immediately and save to current-user
44
- platform preferences explicitly.
45
-
46
- Standard forms use Save draft / Submit. Consume platform authenticated drafts;
47
- do not create application draft tables, localStorage or Nest draft endpoints.
48
- PC drawer controls are Full screen / New page / Close, with a platform draft
49
- handoff to the new page. Mobile entry uses grouped touch-field rows, a simple
50
- title and a fixed action footer, with bottom sheets for drafts and recovery.
51
- Do not add a mobile Back to list action or desktop form controls. Preserve edit
52
- revisions and draft CAS; successful submission consumes the draft atomically.
53
-
54
- ## Optional application backend
55
-
56
- The default scaffold contains Web and shared contracts only. Declare
57
- `backend: { enabled: true }` or application server capabilities, then use the
58
- existing `pnpm openxiangda check` / `pnpm dev` loop. The tool initializes Nest
59
- source and its exact dependencies once, preserves authored source, and retries
60
- an interrupted dependency install. Standard platform workflow activation does
61
- not imply application Nest. See `docs/backend.md` and the runnable
62
- `examples/business-action-extension` for SDK ownership and transaction retries.