openxiangda-skill-kit 2.0.0-alpha.13 → 2.0.0-alpha.131

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 (50) hide show
  1. package/README.md +6 -6
  2. package/dist/bin.js +0 -0
  3. package/dist/index.d.ts +4 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +167 -62
  6. package/dist/index.js.map +1 -1
  7. package/dist/internal/skill-installer.d.ts +8 -0
  8. package/dist/internal/skill-installer.d.ts.map +1 -0
  9. package/dist/internal/skill-installer.js +51 -0
  10. package/dist/internal/skill-installer.js.map +1 -0
  11. package/package.json +3 -6
  12. package/skills/manifest.json +2 -32
  13. package/skills/openxiangda-v2/SKILL.md +57 -24
  14. package/skills/openxiangda-v2/agents/openai.yaml +1 -1
  15. package/skills/openxiangda-v2/references/appspec.md +67 -0
  16. package/skills/openxiangda-v2/references/architecture.md +9 -0
  17. package/skills/openxiangda-v2/references/backend.md +279 -0
  18. package/skills/openxiangda-v2/references/commands.md +21 -0
  19. package/skills/openxiangda-v2/references/data-authz.md +410 -0
  20. package/skills/openxiangda-v2/references/delivery.md +49 -0
  21. package/skills/openxiangda-v2/references/discovery.md +15 -0
  22. package/skills/openxiangda-v2/references/frontend.md +259 -0
  23. package/skills/openxiangda-v2/references/public-access.md +159 -0
  24. package/skills/openxiangda-v2/references/testing.md +74 -0
  25. package/skills/openxiangda-v2/references/workflow-events.md +279 -0
  26. package/skills/openxiangda-v2/references/workspace.md +48 -0
  27. package/docs/architecture/repository-and-release.md +0 -52
  28. package/docs/backend.md +0 -89
  29. package/docs/concepts.md +0 -34
  30. package/docs/data-authz.md +0 -100
  31. package/docs/delivery.md +0 -71
  32. package/docs/frontend.md +0 -47
  33. package/docs/getting-started.md +0 -120
  34. package/docs/index.md +0 -23
  35. package/docs/llms.txt +0 -12
  36. package/docs/reference/cli.md +0 -51
  37. package/docs/reference/mcp.md +0 -26
  38. package/docs/workflow-events.md +0 -63
  39. package/skills/openxiangda-v2-architecture/SKILL.md +0 -29
  40. package/skills/openxiangda-v2-architecture/agents/openai.yaml +0 -4
  41. package/skills/openxiangda-v2-backend/SKILL.md +0 -42
  42. package/skills/openxiangda-v2-backend/agents/openai.yaml +0 -4
  43. package/skills/openxiangda-v2-data-authz/SKILL.md +0 -44
  44. package/skills/openxiangda-v2-data-authz/agents/openai.yaml +0 -4
  45. package/skills/openxiangda-v2-delivery/SKILL.md +0 -63
  46. package/skills/openxiangda-v2-delivery/agents/openai.yaml +0 -4
  47. package/skills/openxiangda-v2-frontend/SKILL.md +0 -40
  48. package/skills/openxiangda-v2-frontend/agents/openai.yaml +0 -4
  49. package/skills/openxiangda-v2-workflow-events/SKILL.md +0 -39
  50. package/skills/openxiangda-v2-workflow-events/agents/openai.yaml +0 -4
@@ -1,38 +1,71 @@
1
1
  ---
2
2
  name: openxiangda-v2
3
- description: Build, inspect, validate, and deliver a complete OpenXiangda 2.0 application workspace. Use when a task spans the React frontend, NestJS backend, Data API, authorization, workflow, events, or whole-application delivery.
3
+ description: Use when researching, designing, building, testing, or delivering an OpenXiangda 2.0 application, including anonymous public forms and other no-account external-user pages.
4
4
  ---
5
5
 
6
6
  # OpenXiangda 2.0
7
7
 
8
- Treat the repository as one typed application product. Work through package contracts and deterministic commands; do not mutate platform resources one at a time.
8
+ Treat requirement discovery, architecture, authorization, development, testing and delivery as one evidence-backed application lifecycle. OpenXiangda 2.0 has no compatibility surface: replace an incorrect alpha contract directly and never add aliases, migration branches, 1.x SDD, Umi, ProComponents, Function CRUD, application-owned identity switching or another identity path.
9
9
 
10
- ## Start Here
10
+ Before a workspace exists, use this Skill's exact npm version:
11
11
 
12
- 1. Run `openxiangda app info` and inspect the returned workspace and platform contract versions.
13
- 2. Read `openxiangda.config.ts`, then identify which domain skill applies:
14
- - architecture: `$openxiangda-v2-architecture`
15
- - frontend: `$openxiangda-v2-frontend`
16
- - backend: `$openxiangda-v2-backend`
17
- - data and authorization: `$openxiangda-v2-data-authz`
18
- - workflow and events: `$openxiangda-v2-workflow-events`
19
- - delivery: `$openxiangda-v2-delivery`
20
- 3. Keep generated contracts current with `openxiangda generate`.
21
- 4. Before delivery, run `openxiangda check` and `openxiangda test`.
12
+ ```bash
13
+ pnpm dlx openxiangda@__OPENXIANGDA_VERSION__ login --base-url <platform>
14
+ pnpm dlx openxiangda@__OPENXIANGDA_VERSION__ create <directory>
15
+ ```
22
16
 
23
- For a new repository, link it to the target platform and let an authorized platform administrator run `openxiangda app provision` once. The command is idempotent and provisions only a native 2.0 application identity.
17
+ Inside an application, use only its locked executable through `pnpm openxiangda`. Never invoke a bare global `openxiangda`, because that executable may belong to stable 1.x. Moving tags such as `latest` and `alpha` are forbidden. To install or refresh this same Skill from the package, run:
24
18
 
25
- ## Boundaries
19
+ ```bash
20
+ pnpm dlx openxiangda@__OPENXIANGDA_VERSION__ skill install --force
21
+ ```
26
22
 
27
- - Put browser code in `apps/web`, server code in `apps/server`, shared domain types in `packages/domain`, and generated declarations in `packages/contracts`.
28
- - Persist business records only through Data API or an App API implemented by the application backend.
29
- - Treat the active role and context attributes as explicit request state.
30
- - Use durable event consumers for side effects and idempotency keys for retries.
31
- - Build one immutable application package; the platform owns deployment execution, health gates, promotion, and rollback.
32
- - Reject any workspace that does not declare the native 2.0 application configuration.
23
+ Use this order:
33
24
 
34
- ## Delivery Gate
25
+ 1. Read [Commands](references/commands.md) when selecting a CLI operation. It is generated from the executable registry. `create` owns application binding and initialization, while `check`, `deploy` and `status` expose the current workspace and deployment facts.
26
+ 2. Read [Discovery](references/discovery.md), turn the request into actors, scenarios, data, permissions, constraints and acceptance evidence, and identify every unresolved choice.
27
+ 3. If the workspace contains `appspec/`, the user asks to maintain requirements, or the task changes observable behavior, read [AppSpec](references/appspec.md), load the bounded index with `pnpm openxiangda spec context --json`, then select only the relevant stable ID. AppSpec is optional and never a release gate; L0 changes create no record.
28
+ 4. Read [Architecture](references/architecture.md), assign one owner to each capability and keep ordinary CRUD on platform Data API.
29
+ 5. For every resource or permission change, read [Data and authorization](references/data-authz.md) before editing `openxiangda.config.ts`.
30
+ When a custom authenticated page lets business users maintain application-role memberships or delegate that maintenance authority, also read [Frontend](references/frontend.md). Use the current-user role-management browser SDK documented there; never add actor selection, an application-owned grant table, or a proxy authorization service.
31
+ 6. If the request mentions external users without platform accounts, anonymous or guest access, a public form/page, resumable submission, duplicate checks, public uploads, or letting a visitor read their own submissions, read [Anonymous public access](references/public-access.md). Use the declared platform contract; do not invent a guest role, public Native Data API, fingerprint identity or application-owned token.
32
+ 7. Read [Frontend](references/frontend.md) for pages or fields and [Backend actions](references/backend.md) only for a real business action.
33
+ When the request customizes the application Todo center or the common PC/mobile frame around standard user pages, use only `standardUserSurfaces` from that reference. The platform must keep ownership of the Router, `RuntimeBoundary`, current user, Notification Hub and Workflow controllers.
34
+ 8. When the application explicitly enables standard approval, application events or notifications, read [Workflow, Events and Notification Hub](references/workflow-events.md). Keep them optional and out of ordinary CRUD.
35
+ 9. Run the connected loop with `pnpm openxiangda dev`, then follow [Testing](references/testing.md) and `pnpm openxiangda check`; check performs the target platform's read-only Native compatibility preflight before application checks or builds.
36
+ 10. Follow [Delivery](references/delivery.md): deploy preproduction first, inspect status/logs, then promote the exact successful version to production.
35
37
 
36
- Use `openxiangda build --backend-image <immutable-image>` only after checks pass. Use `openxiangda deploy`, `openxiangda status`, and `openxiangda logs` only when the user has authorized the environment change.
38
+ For an AI-native client, start the workspace protocol through the same pinned executable:
37
39
 
38
- Read [Getting Started](../../docs/getting-started.md) for the complete development loop.
40
+ ```bash
41
+ pnpm exec openxiangda --mcp-stdio --cwd <workspace>
42
+ ```
43
+
44
+ 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, read `openxiangda://workspace/appspec` or call `appspec_context`; treat it as advisory context, never as deployment authority. Require `aiCatalog` and `aiCatalogDigest` to match the normal compiler output. Never create an application MCP server, Catalog file, preview store or AI authorization path.
45
+
46
+ Authenticated browser routes use the current logged-in user's complete application-role union. An application that serves every logged-in platform user may declare one existing package role through `authz.authenticatedUserRoleCode`; the platform materializes that role on first access and unions it with manual and business-projected roles. Do not fake the role in React, use a membership resource for a universal audience, or confuse the baseline access role with business membership. The only no-account exception is an exact static user route declared through `frontend.publicAccess`; it uses a platform-issued HttpOnly browser credential and dedicated `createAnonymousPublicClient` operations. It is not an application role, login session or general Data API identity. For both paths the platform remains authoritative for capabilities, fields, rows and RLS. A declared Perspective is only a read projection over the authenticated union: it may narrow pages, rows and readable fields, but never create/update/delete, workflow or custom-action authorization. Omitted Perspective means the complete union. Application code does not replace either identity, persist a platform token, construct an authorization result or keep a second permission state.
47
+
48
+ Applications own one explicit typed admin navigation declaration. The compiler
49
+ owns page/route generation, but it never turns every generated resource or
50
+ Workflow into a menu item; the Shell renders only declared navigation and then
51
+ filters it by authorization. Keep page existence, route reachability and menu
52
+ visibility separate. Use resource mutation ownership and per-operation
53
+ generated surface flags, and give only self-contained `standalone` Workflow
54
+ launch pages a menu entry.
55
+
56
+ Read only the references needed for the current change, except the mandatory discovery and Data/authorization rules above:
57
+
58
+ - [Discovery](references/discovery.md)
59
+ - [AppSpec](references/appspec.md)
60
+ - [Commands](references/commands.md)
61
+ - [Architecture](references/architecture.md)
62
+ - [Workspace agent contract](references/workspace.md)
63
+ - [Backend actions](references/backend.md)
64
+ - [Data and authorization](references/data-authz.md)
65
+ - [Anonymous public access](references/public-access.md)
66
+ - [Frontend](references/frontend.md)
67
+ - [Workflow, Events and Notification Hub](references/workflow-events.md)
68
+ - [Testing](references/testing.md)
69
+ - [Delivery](references/delivery.md)
70
+
71
+ Workflow and Notification Hub are optional 2.0 modules outside the CRUD golden path. Never add their packages, routes, declarations or runtime workers to an application that does not enable them.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "OpenXiangda 2.0"
3
- short_description: "Build and deliver typed OpenXiangda 2.0 applications"
3
+ short_description: "Build internal and anonymous public OpenXiangda 2.0 apps"
4
4
  default_prompt: "Use $openxiangda-v2 to build and validate this OpenXiangda 2.0 application."
@@ -0,0 +1,67 @@
1
+ # AppSpec
2
+
3
+ AppSpec 是 OpenXiangda 2.0 应用仓库中的可选业务意图层。它帮助 AI 理解“为什么做、应当表现成
4
+ 什么、这次改变什么”,但不复制 `openxiangda.config.ts`、编译合同、代码、测试或部署状态,也
5
+ 不是发布门禁。
6
+
7
+ AppSpec 只使用 2.0 的 `appspec/`、文档 schema v1 与 context v2。绝不读取、识别、导入或迁移
8
+ 1.x 的 `openspec/`、SDD、表单、页面、函数和发布材料。
9
+
10
+ ## 何时读取
11
+
12
+ - 工作区存在 `appspec/` 时,在分析需求或修改行为前运行 `pnpm openxiangda spec context --json`,先读取
13
+ 有界索引。
14
+ - 从索引选择相关变更、能力、ADR 或历史变更 ID,再运行例如
15
+ `pnpm openxiangda spec context booking-window --json` 读取正文。
16
+ - AI-native 客户端可读取 `openxiangda://workspace/appspec` 或调用 `appspec_context`。
17
+ - 默认不加载全部正文;历史变更只在追查原因时用稳定 ID 按需读取。
18
+
19
+ ## 何时维护
20
+
21
+ | 风险 | 典型变化 | 最小维护量 |
22
+ | --- | --- | --- |
23
+ | L0 | 重构、格式、无行为变化 | 不创建 ChangeSpec |
24
+ | L1 | 文案、单字段、局部规则 | 一份短 ChangeSpec |
25
+ | L2 | 跨资源、权限、状态变化 | 加正反验收、数据与回滚 |
26
+ | L3 | 身份、迁移、并发、外部副作用 | 加失败/幂等、资源边界和 ADR |
27
+
28
+ 用户没有要求且变更是 L0 时,不要为了“流程完整”创建文档。小应用可以长期只维护 `app.md`。
29
+
30
+ ## 标准操作
31
+
32
+ ```bash
33
+ pnpm openxiangda spec init
34
+ pnpm openxiangda spec add-capability CAP-BOOKING --title "预约管理" --resources bookings
35
+ pnpm openxiangda spec new booking-window --title "限制可预约时段" --risk L1 \
36
+ --capabilities CAP-BOOKING --resources bookings
37
+ pnpm openxiangda spec context booking-window --json
38
+ pnpm openxiangda spec check
39
+ pnpm openxiangda spec close booking-window --current-spec merged \
40
+ --summary "已通过真实角色验收"
41
+ ```
42
+
43
+ `spec check` 是用户主动选择的严格检查;普通 `check/dev/deploy` 只给 advisory,不因 AppSpec
44
+ 缺失或不完整而失败。
45
+
46
+ 新 ChangeSpec 的 `currentSpec` 固定从 `pending` 开始。关闭前先把长期有效结果合入 `app.md` 或相关
47
+ CapabilitySpec,再由用户确认 `merged`;不改变长期规格时由用户确认 `not-applicable`。AI 可以列出
48
+ 未合入差异,但不能自行选择这两个值。稳定 REQ/AC 未进入当前规格、L2/L3 必需章节为空或保留默认
49
+ 占位内容时,`spec close` 不移动文件。
50
+
51
+ ## 写作边界
52
+
53
+ - `app.md` 记录长期目标、角色、范围、术语和跨能力约束。
54
+ - `capabilities/CAP-*.md` 记录当前有效需求,使用稳定 `REQ-*` 和可证伪 `AC-*`。
55
+ - `changes/active/*.md` 只记录本次 delta、风险、影响范围与验收;关闭前把仍有效的结论合入能力规格,
56
+ 并由用户确认 `currentSpec`。
57
+ - `decisions/ADR-*.md` 只用于架构显著决定,不为普通小改创建。
58
+ - 引用真实 2.0 resource/action code,不抄字段定义和权限矩阵的机器事实。
59
+ - AI 可以创建 `draft`、指出未确认问题和建议更新,不能自行把需求标记为 `confirmed`。
60
+ - 不因 AppSpec 与实现冲突就静默修改其中一方;明确指出冲突,由用户决定业务意图,技术事实以实时
61
+ 编译合同为准。
62
+
63
+ ## 可迁移程度
64
+
65
+ 完整 AppSpec 能显著提高另一个新项目复现业务逻辑的概率,但最终文档本身不等于可执行应用。
66
+ 需要接近原应用时,应同时提供 AppSpec、`openxiangda.config.ts`、必要自定义动作、测试和外部接口
67
+ 合同;历史 ChangeSpec 不是必需输入。
@@ -0,0 +1,9 @@
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.
@@ -0,0 +1,279 @@
1
+ # OpenXiangda 2.0 Backend
2
+
3
+ Read [Architecture](architecture.md) and [Data and authorization](data-authz.md)
4
+ before adding a backend. Finish with [Testing](testing.md).
5
+
6
+ Keep normal record CRUD in the platform Data API. Use `apps/server` only for typed business actions that need server-side logic. For an interactive request, the platform gateway supplies the verified current user, complete application-role union and capabilities. Controllers bind operations and capabilities through `openxiangda/nest`; workers continue to use their explicit application identity.
7
+
8
+ Every interactive business action uses `@OpenXiangdaOperation(operation)`. The
9
+ guard checks `operation.requiredCapability` once at App API ingress. Inside that
10
+ declared action, inject `OpenXiangdaBusinessDataApiService`,
11
+ `OpenXiangdaBusinessNotificationService`, or use
12
+ `OpenXiangdaStandardOperations`: Native Data and Notification Hub then run as the trusted backend
13
+ for this exact application, environment, version and operation. Do not forward
14
+ the caller's resource, row or field permissions into that internal Data call,
15
+ and do not author an `authorizationJSON` permission mirror. The platform still
16
+ records the initiating user and action code in records, files, events and audit.
17
+ The platform models this internal hop as the request-bounded
18
+ `native-business-action` principal. It is neither an OAuth client nor a Native
19
+ runtime credential: PostgreSQL accepts it only after the platform has verified
20
+ the signed Gateway invocation, exact target/Head, published
21
+ `code + requiredCapability`, application data scope and initiating user. Never
22
+ copy an invocation token into application data, construct the action headers
23
+ yourself, or fall back to `OpenXiangdaApplicationDataApiService` when this
24
+ channel is rejected; a rejection means the platform/package combination is not
25
+ compatible or the invocation is stale.
26
+
27
+ For an informational business notification, inject
28
+ `OpenXiangdaBusinessNotificationService` in the same declared action. The SDK
29
+ uses the short-lived gateway invocation, never the caller's original login
30
+ token. The platform rechecks the operation and capability, promotes the call to
31
+ the exact application/environment principal, and records the initiating user
32
+ and action. Do not grant ordinary users `app:notification2:send` and do not use
33
+ the privileged `OpenXiangdaNotificationService` as an application workaround.
34
+
35
+ ```ts
36
+ import { Inject } from '@nestjs/common';
37
+ import {
38
+ OPENXIANGDA_NOTIFICATION_BUSINESS_SEND_V2,
39
+ OPENXIANGDA_NOTIFICATION_EVENT_SEND_V2,
40
+ OpenXiangdaBusinessNotificationService,
41
+ } from 'openxiangda/nest';
42
+
43
+ constructor(
44
+ @Inject(OpenXiangdaBusinessNotificationService)
45
+ private readonly notifications: OpenXiangdaBusinessNotificationService,
46
+ ) {}
47
+
48
+ await this.notifications.send({
49
+ schemaVersion: OPENXIANGDA_NOTIFICATION_BUSINESS_SEND_V2,
50
+ eventId: body.eventId,
51
+ correlationId: `proposal:${proposal.id}`,
52
+ messageKey: `proposal:${proposal.id}:invite:${member.id}`,
53
+ sourceSequence: proposal.revision,
54
+ recipients: [{ userId: member.userId }],
55
+ title: '待您附议',
56
+ summary: proposal.title,
57
+ navigationTarget: {
58
+ kind: 'APP_ROUTE',
59
+ appCode: 'proposal_app',
60
+ routeCodes: {
61
+ desktop: 'proposal.detail',
62
+ mobile: 'proposal.detail-mobile',
63
+ },
64
+ pathParams: { id: proposal.id },
65
+ access: 'AUTHENTICATED',
66
+ },
67
+ idempotencyKey: `proposal:${proposal.id}:invite:${member.id}:v${proposal.revision}`,
68
+ });
69
+
70
+ ```
71
+
72
+ The fixed template accepts only `title` and optional `summary`. Reuse the same
73
+ `eventId` or `idempotencyKey` for retries. Reuse `messageKey` with increasing
74
+ `sourceSequence` for state convergence; do not generate a new logical message
75
+ for each retry.
76
+
77
+ The generated
78
+ `notificationTemplateCodes.applicationInformational` constant documents the
79
+ selected platform template; `send()` chooses it automatically and does not
80
+ accept a caller-supplied template code.
81
+
82
+ Inside a signed Application Events/date-trigger handler, call
83
+ `sendFromEvent()` on the same service. The SDK obtains the application
84
+ credential and forwards the current eventId, deliveryId and subscription code.
85
+ Declare only dot paths beneath the immutable event `data`; Notification Hub
86
+ resolves the actual Native user IDs and presentation text. Do not copy values
87
+ from the event into a caller-supplied `recipients` array and never manufacture a
88
+ user token.
89
+
90
+ ```ts
91
+ await this.notifications.sendFromEvent({
92
+ schemaVersion: OPENXIANGDA_NOTIFICATION_EVENT_SEND_V2,
93
+ correlationId: `proposal:${event.data.proposalId}`,
94
+ messageKey: `proposal:${event.data.proposalId}:reminder`,
95
+ sourceSequence: event.data.revision,
96
+ recipientPaths: ['assigneeUserIds'],
97
+ titlePath: 'notification.title',
98
+ summaryPath: 'notification.summary',
99
+ navigationTarget: {
100
+ kind: 'APP_ROUTE',
101
+ appCode: 'proposal_app',
102
+ routeCodes: {
103
+ desktop: 'proposal.detail',
104
+ mobile: 'proposal.detail-mobile',
105
+ },
106
+ pathParams: { id: event.data.proposalId },
107
+ access: 'AUTHENTICATED',
108
+ },
109
+ idempotencyKey: event.id,
110
+ });
111
+ ```
112
+
113
+ `OpenXiangdaDataApiService` deliberately retains ordinary current-user Data
114
+ authorization and automatically inherits the request Perspective for reads.
115
+ Its writes still authorize against the complete role union. Do not use it to
116
+ implement a declared business action. The
117
+ business facade also rejects routes without immutable `OpenXiangdaOperation`
118
+ metadata, so a controller cannot silently gain trusted Data access.
119
+
120
+ If a custom controller bypasses the standard Data API and reads Nest-owned SQL,
121
+ an external service or another datastore, inject `@CurrentPerspective()` and
122
+ apply the declared role-based read projection explicitly. Perspective is not a
123
+ credential and must never change mutation authorization. Prefer moving ordinary
124
+ reads back to `OpenXiangdaDataApiService`, where the platform owns the contract.
125
+
126
+ Do not duplicate resource persistence in controllers. Keep actions bounded and side-effect behavior explicit. Run `pnpm openxiangda dev` for the connected loop and `pnpm openxiangda check` before delivery.
127
+
128
+ Keep the generated `apps/server/src/main.ts` on
129
+ `bootstrapOpenXiangdaApplication(AppModule)`. Do not replace it with an
130
+ application-owned `NestFactory` or `FastifyAdapter`: the SDK bootstrap owns the
131
+ exact raw request bytes required by gateway signatures, proxy trust, shutdown
132
+ hooks, and the runtime listen contract.
133
+
134
+ Connected development and release builds deliberately use different compilers,
135
+ so application Nest classes must never depend on emitted constructor metadata.
136
+ Every constructor dependency in a `@Controller` or `@Injectable` class uses an
137
+ explicit token:
138
+
139
+ ```ts
140
+ import { Controller, Inject, Post } from '@nestjs/common';
141
+ import { appOperations } from '@app/contracts';
142
+ import {
143
+ OpenXiangdaBusinessDataApiService,
144
+ OpenXiangdaOperation,
145
+ } from 'openxiangda/nest';
146
+
147
+ @Controller()
148
+ export class VisitorReservationsController {
149
+ constructor(
150
+ @Inject(OpenXiangdaBusinessDataApiService)
151
+ private readonly data: OpenXiangdaBusinessDataApiService,
152
+ ) {}
153
+
154
+ @Post('/api/reservations/enroll')
155
+ @OpenXiangdaOperation(appOperations.reservationEnroll)
156
+ async enroll() {
157
+ return await this.data.create('visitor-reservations', { status: 'pending' });
158
+ }
159
+ }
160
+ ```
161
+
162
+ `openxiangda check` and `openxiangda dev` reject implicit constructor injection.
163
+ Do not silence that diagnostic or replace the connected-development compiler.
164
+
165
+ Backend routes are private to the application unless their operation declaration contains an explicit `ai` block. Never infer AI exposure or risk from the HTTP method. A write action declares its touched resources and non-empty side effects, for example:
166
+
167
+ Build operation request and response schemas through
168
+ `resourceRecordSchema`, `schemaRef` and `composeAppOperationSchemas` from
169
+ `openxiangda/config`. These helpers project canonical field value protocols
170
+ from the same Resource declaration and prevent repeated user, department,
171
+ resource-reference or managed-file shapes. Do not import a physical toolchain
172
+ package or copy the JSON protocol by hand.
173
+
174
+ ```ts
175
+ {
176
+ code: 'reservation.enroll',
177
+ method: 'POST',
178
+ path: '/api/reservations/enroll',
179
+ capability: 'app:visitor-app:reservation:enroll',
180
+ ...composeAppOperationSchemas({
181
+ request: resourceRecordSchema(visitorReservations, {
182
+ fields: ['visitorName', 'visitDate', 'startTime'],
183
+ }),
184
+ response: schemaRef('ReservationRecord'),
185
+ definitions: {
186
+ ReservationRecord: resourceRecordSchema(visitorReservations),
187
+ },
188
+ }),
189
+ ai: {
190
+ name: '预约访客',
191
+ description: '校验预约规则并创建访客预约',
192
+ risk: 'write',
193
+ resources: ['reservations'],
194
+ sideEffects: ['reservations:create'],
195
+ concurrency: 'none',
196
+ timeoutMs: 10000,
197
+ },
198
+ }
199
+ ```
200
+
201
+ Every custom operation capability is declared once in
202
+ `authz.capabilities` with `kind: 'backend'`, then referenced by the operation
203
+ and granted to every role allowed to call it. A capability string written only
204
+ on an operation or role is invalid. Resource CRUD capabilities remain generated
205
+ and must not be copied into this catalog.
206
+
207
+ Generated CRUD and custom actions use the same platform Catalog, current-user authorization, Preview/Confirm boundary and idempotent execution path. Do not create an application-owned MCP server, preview store, AI role or database client.
208
+
209
+ For visitor duplicates, meeting-time conflicts, and course capacity, use
210
+ `OpenXiangdaStandardOperations`. These helpers submit the whole rule as one
211
+ platform transaction. Do not query mutable rows before calling them and do not
212
+ calculate replacement counters in application code. Course selection is a
213
+ locked `record-assert` plus a bounded `increment`; replay must reach the
214
+ platform idempotency receipt even after the course becomes full.
215
+
216
+ The visitor helper has one exact command shape. `duplicateMatch` is the actual
217
+ field-to-submitted-value object used for equality matching; it is never a list
218
+ of field names:
219
+
220
+ ```ts
221
+ return this.operations.createVisitorReservation({
222
+ reservationResourceCode: 'visitor-reservations',
223
+ duplicateMatch: {
224
+ visitorIdentityNumber: body.visitorIdentityNumber,
225
+ visitDate: body.visitDate,
226
+ startTime: body.startTime,
227
+ },
228
+ data: {
229
+ visitorIdentityNumber: body.visitorIdentityNumber,
230
+ visitDate: body.visitDate,
231
+ startTime: body.startTime,
232
+ status: body.status,
233
+ },
234
+ idempotencyKey: body.idempotencyKey,
235
+ activeStatusField: 'status',
236
+ activeStatusValues: ['pending', 'approved'],
237
+ });
238
+ ```
239
+
240
+ Use only declared field codes in `duplicateMatch`. Its values and `data` come
241
+ from the same immutable request body.
242
+
243
+ An idempotency key and the complete command payload are one immutable pair.
244
+ Never put `new Date()`, `Date.now()`, `randomUUID()` or another changing value
245
+ inside a controller while constructing the transaction. If a business time is
246
+ part of the record, declare it as a required operation request field; the
247
+ caller creates it once together with the idempotency key and reuses both on
248
+ every retry. Platform-created record timestamps remain the source of truth
249
+ when no separate business time is required.
250
+
251
+ The Nest SDK automatically carries the platform Gateway `x-request-id` from a
252
+ verified Named Action into its nested Data API, directory, workflow, and
253
+ notification calls. Do not generate or replace that header in application
254
+ code. If a strict authorization projection is temporarily unavailable, keep
255
+ the complete command and `idempotencyKey` unchanged when retrying after the
256
+ platform reports recovery: the Native transaction receipt returns the already
257
+ committed items with `replayed: true` and does not repeat mutations or events.
258
+ `OPENXIANGDA_AUTHORIZATION_PROJECTION_NOT_READY` is a fail-closed, retryable
259
+ platform readiness response; it is not evidence that an earlier idempotent
260
+ business transaction was rolled back. Diagnose it with the returned request id.
261
+
262
+ When declaring a lower-level transaction directly, use only the current
263
+ contract:
264
+
265
+ - every guard has a stable `OPENXIANGDA_*` `errorCode`;
266
+ - `query-empty` protects a business-key uniqueness rule;
267
+ - `record-exists` requires one visible record without forcing a mutation of it;
268
+ - `record-match` requires one visible record plus typed value/field assertions,
269
+ and may protect a mutation of another resource;
270
+ - `record-assert` locks one exact record and checks declared field values or
271
+ declared field-to-field comparisons;
272
+ - `databaseNowAssertion('publishAt', 'lte')` compares a declared `datetime`
273
+ field with the single PostgreSQL transaction time. Do not pass `Date.now()`,
274
+ an offset, a SQL function or a fallback timestamp; a successful idempotent
275
+ replay returns the original `evaluatedAt` without re-evaluating the guard;
276
+ - `increment` targets one declared integer field and must have a matching
277
+ same-resource, same-record `record-assert` guard;
278
+ - never send SQL, table names, expressions, old guards without `errorCode`, or
279
+ a pre-read/absolute-update fallback.
@@ -0,0 +1,21 @@
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 需求与变更辅助 |