openxiangda-skill-kit 2.0.0-alpha.91 → 2.0.0-alpha.94

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda-skill-kit",
3
- "version": "2.0.0-alpha.91",
3
+ "version": "2.0.0-alpha.94",
4
4
  "description": "Validation and deterministic packaging for OpenXiangda 2.0 AI skills.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,7 +17,7 @@
17
17
  "README.md"
18
18
  ],
19
19
  "dependencies": {
20
- "openxiangda-devkit-core": "2.0.0-alpha.72"
20
+ "openxiangda-devkit-core": "2.0.0-alpha.75"
21
21
  },
22
22
  "devDependencies": {
23
23
  "tsx": "4.23.12",
@@ -38,10 +38,18 @@ For an AI-native client, start the workspace protocol through the same pinned ex
38
38
  pnpm exec openxiangda --mcp-stdio --cwd <workspace>
39
39
  ```
40
40
 
41
- Read `openxiangda://workspace/contracts` or call `contract_describe`. 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.
41
+ 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.
42
42
 
43
43
  The browser uses the current logged-in user's complete application-role union. The platform is authoritative for capabilities, fields, rows and RLS. A declared Perspective is only a read projection over that 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 the authenticated identity, persist a platform token, construct an authorization result or keep a second permission state.
44
44
 
45
+ Applications own one explicit typed admin navigation declaration. The compiler
46
+ owns page/route generation, but it never turns every generated resource or
47
+ Workflow into a menu item; the Shell renders only declared navigation and then
48
+ filters it by authorization. Keep page existence, route reachability and menu
49
+ visibility separate. Use resource mutation ownership and per-operation
50
+ generated surface flags, and give only self-contained `standalone` Workflow
51
+ launch pages a menu entry.
52
+
45
53
  Read only the references needed for the current change, except the mandatory discovery and Data/authorization rules above:
46
54
 
47
55
  - [Discovery](references/discovery.md)
@@ -243,6 +243,10 @@ contract:
243
243
  and may protect a mutation of another resource;
244
244
  - `record-assert` locks one exact record and checks declared field values or
245
245
  declared field-to-field comparisons;
246
+ - `databaseNowAssertion('publishAt', 'lte')` compares a declared `datetime`
247
+ field with the single PostgreSQL transaction time. Do not pass `Date.now()`,
248
+ an offset, a SQL function or a fallback timestamp; a successful idempotent
249
+ replay returns the original `evaluatedAt` without re-evaluating the guard;
246
250
  - `increment` targets one declared integer field and must have a matching
247
251
  same-resource, same-record `record-assert` guard;
248
252
  - never send SQL, table names, expressions, old guards without `errorCode`, or
@@ -62,6 +62,16 @@ also granted only to the intended roles. They are owned and exported by the
62
62
  generated field policy, so do not repeat them in `authz.capabilities`.
63
63
  `authz.capabilities` contains only explicit `backend` or `ui` capabilities.
64
64
 
65
+ Declare mutation ownership on the same resource with
66
+ `mutationOwner: 'native' | 'action' | 'readonly' | 'workflow'`. Native defaults
67
+ to generated list/detail/create/update/delete. Other owners default to readable
68
+ list/detail only and must use their named action or Workflow boundary for
69
+ mutation. Use `generated: { list, detail, create, update, delete }` to narrow
70
+ the generated page/operation surface. The compiler rejects Native mutation
71
+ pages, AI operations or role grants on a non-Native owner, and rejects
72
+ create/update when no writable business field exists. Do not grant a resource
73
+ create/update/delete capability to make an action-owned record editable.
74
+
65
75
  Declare optional work Perspectives once at the application root. A Perspective
66
76
  references existing role codes; the compiler derives its readable capability
67
77
  projection, so never hand-author `capabilityCodes` or duplicate row filters:
@@ -99,6 +109,40 @@ generated UI and platform field policies.
99
109
 
100
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.
101
111
 
112
+ Numeric bounds belong on the field declaration and are enforced by the
113
+ platform for every write path. They are inclusive, and only valid on
114
+ `number.integer` or `number.decimal`:
115
+
116
+ ```ts
117
+ { code: 'capacity', type: 'number.integer', label: '容量', required: true, min: 0, max: 10000 }
118
+ ```
119
+
120
+ Cross-field rules belong on the resource, not in a generated form callback.
121
+ Only bounded same-record field comparisons are supported; declare multiple
122
+ invariants when all must hold:
123
+
124
+ ```ts
125
+ {
126
+ code: 'sessions',
127
+ name: '场次',
128
+ fields: [
129
+ { code: 'startAt', type: 'datetime', label: '开始时间', required: true },
130
+ { code: 'endAt', type: 'datetime', label: '结束时间', required: true },
131
+ { code: 'capacity', type: 'number.integer', label: '容量', required: true, min: 0 },
132
+ { code: 'occupied', type: 'number.integer', label: '已占用', required: true, min: 0 },
133
+ ],
134
+ invariants: [
135
+ { code: 'time-order', expression: { leftField: 'startAt', operator: 'lt', rightField: 'endAt' } },
136
+ { code: 'capacity-not-exceeded', expression: { leftField: 'capacity', operator: 'gte', rightField: 'occupied' } },
137
+ ],
138
+ }
139
+ ```
140
+
141
+ `datetime-range` is always a non-empty half-open interval `[start, end)`.
142
+ Adjacent values such as `[10:00, 11:00)` and `[11:00, 12:00)` do not overlap.
143
+ Use the ordinary `overlaps` query operator or a `query-empty` transaction guard;
144
+ never add or subtract milliseconds at the boundary.
145
+
102
146
  Do not author raw schema or storage words as field types. `string`, `text`,
103
147
  `integer`, `decimal`, `boolean`, `date`, `datetime`, `uuid`, `json` and `file`
104
148
  describe value or storage families only when the platform protocol says so;
@@ -28,21 +28,54 @@ union. Never fetch identity for the header, display role codes, add a second
28
28
  identity selector, or copy Shell and
29
29
  directory-client implementations into application source.
30
30
 
31
- For a custom admin operation page, declare the route in
32
- `openxiangda.config.ts`, import the generated `appRoutes`, and bind every route
33
- key to exactly one local React page with `defineAdminContributions` from
34
- `openxiangda/react`. Pass the result to `OpenXiangdaApplication`. Do not wrap the
35
- page in another `Shell`: the runtime registers it in the unified router and the
36
- same Shell owns its menu entry, title, history and direct-URL capability gate.
37
- Only `navigation: 'menu'` routes enter the menu; hidden routes retain the same
38
- `capability` or `access.allOf/anyOf`, including ancestor constraints. Never add
39
- an application-owned layout, router, menu, identity provider or permission
40
- store. Keep operation pages under `/admin/operations`; parameterized routes are
41
- hidden so they cannot become unresolved menu links.
31
+ The application owns the editable admin information architecture through the
32
+ typed `defineAdminNavigation`, `adminNavigationGroup`, `adminResourcePage`,
33
+ `adminOperationPage`, `adminWorkflowWorkCenterPage` and
34
+ `adminWorkflowLaunchPage` helpers in `openxiangda/config`. The compiler emits
35
+ all reachable `adminPages`, but the Shell renders only pages explicitly
36
+ referenced by generated `adminNavigation`, then applies current-user access as
37
+ a final filter. Page existence, route reachability and menu visibility are
38
+ separate: detail/new/edit/dynamic/handoff pages never become menu entries by
39
+ discovery, and permission logic cannot create entries. Read
40
+ `openxiangda://workspace/contracts` or call `contract_describe`, then copy
41
+ `data.adminNavigationAuthoring.suggestion.expression` once into
42
+ `frontend.admin.navigation` with the listed `openxiangda/config` imports. The
43
+ proposal is deterministic and editable; the compiler/runtime never invokes it
44
+ or appends newly added resources later.
45
+
46
+ Declare custom routes in `openxiangda.config.ts`, import the generated
47
+ `appRoutes`, and bind every route key to exactly one local React page with
48
+ `defineApplicationContributions` from `openxiangda/react`. Pass the result to
49
+ `OpenXiangdaApplication`. The runtime registers `surface: 'admin'` routes inside
50
+ the one platform Shell; do not wrap them in another Shell. It renders
51
+ `surface: 'user'` routes without the admin Shell so mobile/user experiences can
52
+ own their page layout without creating another router, identity provider or
53
+ permission store. Only static admin routes explicitly referenced by
54
+ `frontend.admin.navigation` enter the menu. Hidden routes retain the same
55
+ `capability` or `access.allOf/anyOf`, including ancestor constraints. Keep admin
56
+ operation pages under `/admin/operations`; parameterized routes remain
57
+ reachable but cannot be menu references. `defineAdminContributions` remains an
58
+ admin-only helper and intentionally rejects `user` routes.
59
+
60
+ This is also the whole-application composition contract: pass the resulting
61
+ `contributions` to `OpenXiangdaApplication` and let that component remain the
62
+ only owner of `BrowserRouter`, `RuntimeBoundary`, Refine, generated resource and
63
+ Workflow routes, and the admin Shell. A user page may render an independent PC
64
+ or mobile layout, but it must not add a nested router/Shell or copy generated
65
+ routes. Route parameters continue to come from React Router, and generated
66
+ capability plus ancestor access guards run before the component renders.
67
+
68
+ To expose the standard application-level todo center, add
69
+ `adminApplicationTodoCenterPage()` to an explicit admin navigation group. The
70
+ compiler generates `/admin/todos`; the same runtime exposes the independent
71
+ mobile `/m/todos` page. The page reads only the authenticated user's
72
+ Notification Hub recipient projection and deep-links to the platform-resolved
73
+ target. Do not query Notification Hub management endpoints or recreate a local
74
+ message state store.
42
75
 
43
76
  For a small business action on a generated resource page, use the typed
44
77
  `resources[resourceCode].toolbar`, `.row` or `.detail` slots accepted by
45
- `defineAdminContributions`. Declare a stable action code, label and
78
+ `defineApplicationContributions`. Declare a stable action code, label and
46
79
  capability/access expression. The render context contains only the current
47
80
  resource, already-authorized record or selection, and a bounded `refresh()`;
48
81
  it does not own CRUD, revisions, fields or authorization. Invoke a guarded Nest
@@ -19,6 +19,20 @@ Use these optional modules only when the application has a real standard approva
19
19
 
20
20
  Declare workflow topology and bindings once in `openxiangda.config.ts`. Use only approval, condition and end nodes for the standard module. Keep business data in Data API/App API and pass an immutable `dataRef`, business revision and bounded facts into prepare/start.
21
21
 
22
+ Every definition declares a launch mode: `standalone` for a self-contained
23
+ generated save-and-start page, `custom-page` for an application operation page,
24
+ `hidden-handoff` for a non-menu handoff route, or `work-center-only` when users
25
+ only act on existing tasks. Only `standalone` may be referenced by
26
+ `adminWorkflowLaunchPage`; the generated browser runtime uses the workflow's
27
+ localized definition title, so applications do not maintain code-to-title
28
+ maps. A standalone page receives all durable business context through its
29
+ typed save handler and URL/search contract; it must never depend on transient
30
+ router location state. Work center, task and instance pages remain reachable
31
+ without becoming launch menu entries. Bind the generated metadata with
32
+ `defineWorkflowLaunchContributions(workflowDefinitions, handlers)`; handlers
33
+ only persist the durable business revision, while generated code keeps title
34
+ and launch mode authoritative.
35
+
22
36
  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.
23
37
 
24
38
  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.
@@ -70,6 +84,70 @@ Use a canonical navigation target with `PLATFORM_ROUTE`, `APP_ROUTE` or allowlis
70
84
 
71
85
  The Notification Hub resolves desktop, mobile and channel deep links. The landing page always re-runs platform authentication and Data/AuthZ/Workflow authorization.
72
86
 
87
+ To replace the standard Workflow detail page everywhere, declare both route
88
+ codes on the Workflow definition declaration. The desktop route must be an
89
+ `admin` route, the mobile route must be a `user` route, and both paths contain
90
+ exactly `:instanceId`:
91
+
92
+ ```ts
93
+ frontend: {
94
+ root: 'apps/web',
95
+ routes: [
96
+ {
97
+ code: 'purchase-detail',
98
+ path: '/admin/operations/purchases/:instanceId',
99
+ label: '采购审批详情',
100
+ surface: 'admin',
101
+ },
102
+ {
103
+ code: 'purchase-detail-mobile',
104
+ path: '/m/purchases/:instanceId',
105
+ label: '移动采购审批详情',
106
+ surface: 'user',
107
+ },
108
+ ],
109
+ },
110
+ workflows: {
111
+ definitions: [{
112
+ version: 1,
113
+ definition: purchaseApproval,
114
+ launch: { mode: 'work-center-only' },
115
+ detailRouteCode: {
116
+ desktop: 'purchase-detail',
117
+ mobile: 'purchase-detail-mobile',
118
+ },
119
+ }],
120
+ bindings: [],
121
+ activations: [],
122
+ },
123
+ ```
124
+
125
+ Bind the generated routes with `defineApplicationContributions`. Inside the
126
+ page, read `instanceId` from React Router and optional `taskId` from the query.
127
+ Use `loadWorkflowInstanceSurface`, `loadWorkflowTaskSurface` and
128
+ `loadWorkflowTimeline` from `openxiangda/core`; render only operations returned
129
+ by the Surface. The platform resolves the same declaration for desktop/mobile
130
+ work centers, Notification Hub logical messages, DingTalk cards and other
131
+ channel snapshots. Omitting `detailRouteCode` keeps the standard pages. A
132
+ declared but missing or incompatible route fails closed rather than falling
133
+ back silently.
134
+
135
+ Standard launch entry is also a platform Surface. Desktop uses
136
+ `/admin/workflows/:workflowCode/new` and mobile uses
137
+ `/m/workflows/:workflowCode/start`; both first load
138
+ `loadWorkflowLaunchSurface(workflowCode)`, then use the same durable
139
+ `saveBusinessRevision -> prepareWorkflowStart -> startWorkflow` protocol.
140
+ `custom-page` and `work-center-only` declarations do not expose the standard
141
+ launch Surface. After start, navigate through the device's standard instance
142
+ entry so the current `detailRouteCode` contract can redirect consistently.
143
+
144
+ An application-level inbox is enabled in explicit admin navigation with
145
+ `adminApplicationTodoCenterPage()`. It projects Notification Hub messages and
146
+ per-recipient interaction for the current logged-in user only; it is not the
147
+ Notification Hub management console. Use its `去处理` navigation to enter the
148
+ authorized Workflow or application page, and keep approve/reject/transfer
149
+ commands on the destination Surface.
150
+
73
151
  ## Interactive channel actions
74
152
 
75
153
  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.
@@ -3,8 +3,8 @@
3
3
  - Use only the workspace-pinned CLI: `pnpm openxiangda <command>`. Never invoke a bare global `openxiangda` inside a 2.0 task.
4
4
  - `appspec/` is the optional 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
5
  - Use Vite, React Router, Refine Core and Ant Design. Do not add Umi, ProComponents or another admin shell.
6
- - Keep generated admin routes and custom actions inside the platform Shell. Bind generated `appRoutes` to local operation pages with `defineAdminContributions`, and use only its typed `toolbar`, `row` and `detail` resource slots. Do not create another router, menu, layout, identity provider, permission store or copied CRUD page; route/action access uses capability or `allOf`/`anyOf`, while Data/App API authorization remains server-owned.
7
- - Declare each resource once in `openxiangda.config.ts` with only `code`, `name`, `fields` and optional list/layout/data-policy settings. Each field owns type, label, required state, Surface flags, reference/file metadata and access. Resource codes are lower kebab-case.
6
+ - 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. Use only its 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.
7
+ - Declare each resource once in `openxiangda.config.ts` with only `code`, `name`, `fields` and optional `mutationOwner`, generated/list/layout/data-policy settings. Each field owns type, label, required state, Surface flags, reference/file metadata and access. Resource codes are lower kebab-case. Use `native` for direct Data API mutations, `action`, `readonly` or `workflow` for non-Native ownership; never grant or generate Native mutation for a non-Native owner.
8
8
  - 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`.
9
9
  - 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.
10
10
  - Add NestJS only for a named business action that needs a cross-resource transaction, an invariant or an external side effect.
@@ -20,6 +20,6 @@
20
20
  - `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`.
21
21
  - Derive role grants with `resourceCapabilityCodes(appCode, resourceCode)`. Declare current-user rows only with `currentUserDataPolicy(...)`; do not invent operators, values or alternate current-user spellings.
22
22
  - 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.
23
- - Standard Workflow and Notification Hub are optional 2.0 modules. Enable them only through the canonical `openxiangda.config.ts` declarations and generated clients; never copy 1.x workflow/message code, tables, APIs, templates or callbacks into this workspace. Keep ordinary CRUD and application-specific state machines independent of them.
23
+ - Standard Workflow and Notification Hub are optional 2.0 modules. Enable them only through the canonical `openxiangda.config.ts` declarations and generated clients; declare each Workflow launch as `standalone`, `custom-page`, `hidden-handoff` or `work-center-only`, put only self-contained standalone launch pages in navigation, and bind durable save handlers with `defineWorkflowLaunchContributions` without repeating generated titles. Desktop and mobile standard launch pages load the same canonical Workflow launch Surface. To replace Workflow detail, declare both desktop and mobile `detailRouteCode` values whose routes contain exactly `:instanceId`; Work Center, the application todo center and notification channels consume the platform-resolved paths, while every Surface and command remains server-authorized. Add the standard current-user todo page only with `adminApplicationTodoCenterPage()`; never query Notification Hub management APIs from a user page. Never depend on temporary router location state or copy 1.x workflow/message code, tables, APIs, templates or callbacks into this workspace. Keep ordinary CRUD and application-specific state machines independent of them.
24
24
  - 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.
25
- - 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`; 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.
25
+ - 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.