openxiangda-skill-kit 2.0.0-alpha.100

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.
@@ -0,0 +1,42 @@
1
+ # OpenXiangda 2.0 Delivery
2
+
3
+ Run `pnpm openxiangda check --json`; it defaults to the test target, compiles
4
+ the complete configuration and contract bundles, and asks the target
5
+ platform's advertised `configurationCompatibility` endpoint to run the same
6
+ Native compiler validation used during deployment preparation. This read-only
7
+ preflight happens before workspace checks, tests, builds, Buildx or uploads.
8
+ Use `--environment production` only when explicitly checking that target. A
9
+ failure preserves `pointer`, client contract/schema versions, platform
10
+ version/capability and the required/supported application-contract tuples.
11
+ The capability also publishes the existing 4 MiB configuration, 8 MiB
12
+ contract and 10 MiB total request bounds so oversized input fails before the
13
+ transport layer.
14
+ Never remove generated fields, reduce versions, raise limits, add app-code
15
+ exceptions or introduce 1.x compatibility to make it pass. The platform
16
+ revalidates authoritatively during deployment preparation.
17
+
18
+ After compatibility succeeds, check owns static checks, tests and production
19
+ builds but deliberately does not seal an AppPackage. Its
20
+ `data.sealedArtifact` object and `.openxiangda/build/seal-status.json` make that
21
+ state explicit even when an older `app-package.json` remains on disk. Deploy to
22
+ test with the returned `openxiangda deploy` next command. Deploy owns the
23
+ official application Dockerfile and platform-provided repository target,
24
+ builds and pushes the Nest image, then writes a `sealed` status tied to the new
25
+ immutable package digest. Never ask the developer for an image tag, digest,
26
+ registry password, Docker configuration, or another public build command. If
27
+ Docker, Buildx, repository configuration, or registry login is missing,
28
+ preserve the stable machine error and retry with the same
29
+ `pnpm openxiangda deploy` command after fixing that prerequisite.
30
+
31
+ `pnpm openxiangda accept --plan <file>` is an optional manual preproduction
32
+ test helper. It prepares expiring real identities but does not deploy, seal,
33
+ promote or satisfy any release gate. Use it only when the requested acceptance
34
+ needs real role membership and browser login.
35
+
36
+ Use `pnpm openxiangda status` and `pnpm openxiangda logs` without an ID for the most recent run, or pass an explicit run ID.
37
+
38
+ For the AI-native MCP entrypoint, `build_app` and `deployment_plan` are explicitly unsealed previews. After the user authorizes deployment, call `deploy_app` without any image coordinate; it owns the same automatic Buildx, push, digest and sealing path as the CLI.
39
+
40
+ Applications with Native Data Resources automatically require `data.native-golden-crud`. If deployment returns `OPENXIANGDA_REQUIRED_CAPABILITY_UNAVAILABLE`, preserve the remediation to upgrade the platform and retry the same deploy command. Never remove the requirement, edit the AppPackage, construct a second identity path, or create Function-based CRUD.
41
+
42
+ After a successful test run, deploy the exact same version with `pnpm openxiangda deploy --environment production --from <test-deployment-id>`. Roll back with `pnpm openxiangda rollback --environment production --to <app-version-id>`. The platform owns durable deployment state.
@@ -0,0 +1,15 @@
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.
@@ -0,0 +1,131 @@
1
+ # OpenXiangda 2.0 Frontend
2
+
3
+ Read [Data and authorization](data-authz.md) for every resource or permission
4
+ change and keep the generated [workspace contract](workspace.md) authoritative.
5
+
6
+ Use the official Vite + React Router + Refine Core + Ant Design template. Import the platform-owned runtime from `openxiangda/react`, field controls from `openxiangda/field-kit` and clients/types from `openxiangda/core`. Do not copy these implementations into application source and do not add per-resource function wrappers.
7
+
8
+ Generated desktop lists already own row selection, atomic batch update/delete,
9
+ CSV/XLS/XLSX preview import, export, filters, column settings and density. Keep
10
+ imports at 100 rows per transaction, use exact declared labels or field codes,
11
+ and leave attachment values to the platform managed-file component.
12
+
13
+ Declare searchable and filterable fields on the resource and let the standard
14
+ CRUD renderer compose the search area. Keyword search spans the declared
15
+ searchable fields; the first two filters remain inline and additional filters
16
+ open in the platform's large modal. The renderer also preserves declaration
17
+ order for form fields and removes authorization/debug explanations from the
18
+ business UI. Do not create application-owned search toolbars, field ordering
19
+ maps, permission tips or per-resource CRUD pages for these standard behaviors.
20
+
21
+ Read the current user, complete application-role union and capabilities from the platform runtime authorization endpoint. There is no application-selected active role or identity switch. The standard Shell owns the optional Perspective selector. Use `hasReadCapability` for navigation, list/detail visibility and readable fields; continue to use `hasCapability` for create/update/delete, workflows and custom actions. The standard client sends the selected Perspective automatically, and the server repeats the projection for rows, fields and RLS. Never implement resource-specific frontend filters for it. Strip unauthorized fields from create and update payloads and never treat a disabled input as server authorization. Verify with `pnpm openxiangda check`.
22
+
23
+ Use the standard `Shell` unchanged. It owns the authenticated user's display
24
+ name, account identifier, safe avatar upload, localized application-role names,
25
+ theme controls, Perspective selection and navigation scroll restoration. It
26
+ also owns directory field requests under the current logged-in user's role
27
+ union. Never fetch identity for the header, display role codes, add a second
28
+ identity selector, or copy Shell and
29
+ directory-client implementations into application source.
30
+
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
+ Generated desktop resource CRUD routes use the compiler-owned
47
+ `/admin/resources/<resourceCode>...` namespace and always render inside the
48
+ platform's one Shell. Their independent mobile admin surface is projected from
49
+ the same generated route catalog under `/m/admin/resources/<resourceCode>...`.
50
+ Generated pages consume that catalog for every list/create/detail/edit/back
51
+ navigation; do not reconstruct root resource paths in application code. Root
52
+ and ordinary `/m/...` paths remain available to explicit user routes. The
53
+ compiler rejects an explicit route whose canonical path shape conflicts with
54
+ any explicit or platform-generated route, including dynamic routes that differ
55
+ only by parameter name. Do not add aliases, redirects or route-order branches
56
+ for earlier alpha paths.
57
+
58
+ Declare custom routes in `openxiangda.config.ts`, import the generated
59
+ `appRoutes`, and bind every route key to exactly one local React page with
60
+ `defineApplicationContributions` from `openxiangda/react`. Pass the result to
61
+ `OpenXiangdaApplication`. The runtime registers `surface: 'admin'` routes inside
62
+ the one platform Shell; do not wrap them in another Shell. It renders
63
+ `surface: 'user'` routes without the admin Shell so mobile/user experiences can
64
+ own their page layout without creating another router, identity provider or
65
+ permission store. Only static admin routes explicitly referenced by
66
+ `frontend.admin.navigation` enter the menu. Hidden routes retain the same
67
+ `capability` or `access.allOf/anyOf`, including ancestor constraints. Keep admin
68
+ operation pages under `/admin/operations`; parameterized routes remain
69
+ reachable but cannot be menu references. `defineAdminContributions` remains an
70
+ admin-only helper and intentionally rejects `user` routes.
71
+
72
+ Declare application-owned login visuals with optional
73
+ `frontend.authentication`. The only supported account contract is
74
+ `existing-platform-users-only` with `registration.mode: 'reject'`; use exact
75
+ desktop `/login` and mobile `/m/login`, each pointing to a static protected user
76
+ default route. Import generated `authenticationSurfaces` and bind an exact
77
+ desktop/mobile renderer map alongside `appRoutes`:
78
+
79
+ ```tsx
80
+ const contributions = defineApplicationContributions(
81
+ { routes: appRoutes, authenticationSurfaces },
82
+ {
83
+ pages,
84
+ authentication: {
85
+ applicationLogin: DesktopLogin,
86
+ applicationLoginMobile: MobileLogin,
87
+ },
88
+ },
89
+ );
90
+ ```
91
+
92
+ Renderers receive only `ApplicationLoginSurfaceProps`: safe method descriptors,
93
+ state/error metadata, normalized `returnTo`, and platform callbacks. They never
94
+ receive credentials, provider configuration, OAuth state, tokens, roles, or
95
+ authorization facts. The platform owns the one Router and authentication
96
+ boundary, tokenless v2 facade, Secure HttpOnly session/refresh family and
97
+ logout. Do not put login in `appRoutes`, call v1 auth APIs, persist tokens, or
98
+ translate network/5xx and authenticated 403 states into login. Generated
99
+ `platformAuthManifest` is the independent login QA denominator and does not
100
+ change protected user route counts.
101
+
102
+ This is also the whole-application composition contract: pass the resulting
103
+ `contributions` to `OpenXiangdaApplication` and let that component remain the
104
+ only owner of `BrowserRouter`, `RuntimeBoundary`, Refine, generated resource and
105
+ Workflow routes, and the admin Shell. A user page may render an independent PC
106
+ or mobile layout, but it must not add a nested router/Shell or copy generated
107
+ routes. Route parameters continue to come from React Router, and generated
108
+ capability plus ancestor access guards run before the component renders.
109
+
110
+ To expose the standard application-level todo center, add
111
+ `adminApplicationTodoCenterPage()` to an explicit admin navigation group. The
112
+ compiler generates `/admin/todos`; the same runtime exposes the independent
113
+ mobile `/m/todos` page. The page reads only the authenticated user's
114
+ Notification Hub recipient projection and deep-links to the platform-resolved
115
+ target. Do not query Notification Hub management endpoints or recreate a local
116
+ message state store.
117
+
118
+ For a small business action on a generated resource page, use the typed
119
+ `resources[resourceCode].toolbar`, `.row` or `.detail` slots accepted by
120
+ `defineApplicationContributions`. Declare a stable action code, label and
121
+ capability/access expression. The render context contains only the current
122
+ resource, already-authorized record or selection, and a bounded `refresh()`;
123
+ it does not own CRUD, revisions, fields or authorization. Invoke a guarded Nest
124
+ App Operation for cross-resource transactions, invariants or side effects and
125
+ leave ordinary CRUD on the Native Data API. Do not copy a generated page just
126
+ to insert a button.
127
+
128
+ Generated contracts intentionally emit each resource Surface once in
129
+ `resourceSurfaces`; `resourceDefinitions[code].surface` references that shared
130
+ object. Import the generated runtime definitions normally. Do not serialize,
131
+ inline or duplicate Surface literals in application code.
@@ -0,0 +1,66 @@
1
+ # Testing and Acceptance
2
+
3
+ Run `pnpm openxiangda check --json` after declaration or code changes. It owns
4
+ generation plus a read-only Native compatibility preflight against the test
5
+ platform before static validation, unit tests or production builds. Use
6
+ `--environment production` only for an explicit production-target check.
7
+ Preserve the stable diagnostic and JSON pointer instead of bypassing a failing
8
+ stage. Read
9
+ `data.sealedArtifact` in the machine result. A successful check always reports
10
+ `state: "check-did-not-seal"`, `sealed: false` and
11
+ `usableForDeploy: false`; it may also describe an older package as
12
+ `previousArtifact`. Never treat an existing `.openxiangda/build/app-package.json`
13
+ as output from the current check. Follow the exact `nextCommand`: rerun check
14
+ when diagnostics fail, or run `openxiangda deploy` to build, seal and deploy.
15
+
16
+ For each changed resource, verify the real chain:
17
+
18
+ 1. declaration compiles into one resource, Surface and capability set;
19
+ 2. desktop and mobile render the declared field semantics;
20
+ 3. create, read, update, delete, filtering, export and revision conflict use Native Data API;
21
+ 4. an allowed role succeeds and a denied role fails for operation, field and row paths;
22
+ 5. stored values and audit records match the declared shape;
23
+ 6. PostgreSQL/RLS remains authoritative, including current-user and multi-role-union cases.
24
+
25
+ For platform generator changes, add a representative multi-resource fixture
26
+ (the compatibility corpus fixes 43 resources, complete list/form/detail/mobile
27
+ surfaces, Perspective/AuthZ/Workflow/Event and exactly 161 producers), assert
28
+ the real toolchain generator output remains byte-identical, and pass the same
29
+ fixture through the platform's exported Native validator. Then run the unchanged
30
+ Web dist budget gate. A budget failure is a
31
+ generator/runtime regression to fix; never raise the application budget or copy
32
+ generated contracts into a smaller application-local format. Browser acceptance
33
+ must also cover the large more-filters modal, declaration-order forms, hidden
34
+ developer-only authorization copy, real account/role labels, avatar update and
35
+ left-menu scroll preservation across route changes.
36
+
37
+ Mock and unit tests are useful but do not close remote acceptance. When real
38
+ identity acceptance is needed, create a short-lived local plan and run:
39
+
40
+ ```bash
41
+ pnpm openxiangda accept --plan .openxiangda/acceptance-plan.json --json
42
+ ```
43
+
44
+ The plan is explicit and preproduction-only:
45
+
46
+ ```json
47
+ {
48
+ "schemaVersion": "openxiangda.preproduction-acceptance-plan/v2",
49
+ "environmentKey": "preproduction",
50
+ "expiresInMinutes": 240,
51
+ "actors": [
52
+ { "key": "allowed", "roleCodes": ["resource_admin"] },
53
+ { "key": "denied", "roleCodes": ["resource_viewer"] }
54
+ ]
55
+ }
56
+ ```
57
+
58
+ The result returns real temporary users and one-time login URLs. Treat those
59
+ URLs as secrets, do not commit the plan/result, and let them expire. `accept`
60
+ is an additional manual test facility: it is never called by `check` or
61
+ `deploy`, never becomes a release gate, and a skipped run must not block a
62
+ release. When it is used, capture the exact AppVersion, environment Head,
63
+ positive/negative result, browser behavior and request identifiers. Production
64
+ promotion reuses the exact successful version; it is not another build.
65
+
66
+ For a new toolchain release, also create a completely fresh workspace using only the packed or published `openxiangda` package. Do not use repository-relative imports, unpublished workspace links or machine-installed legacy Skills.
@@ -0,0 +1,180 @@
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. Keep business data in Data API/App API and pass an immutable `dataRef`, business revision and bounded facts into prepare/start.
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
+
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.
37
+
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.
39
+
40
+ ## Submission and concurrency
41
+
42
+ 1. Persist the business draft through Data API/App API.
43
+ 2. Bind prepare to the saved revision and all submission answers.
44
+ 3. Resolve required organization/approver choices.
45
+ 4. Start with a stable idempotency key distinct from the draft-save key.
46
+ 5. Use the Surface revision/concurrency token for every command.
47
+
48
+ 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.
49
+
50
+ ## Notification profile
51
+
52
+ A node may reference a notification profile that selects:
53
+
54
+ - source facts that create, update or close a message;
55
+ - audiences such as current participant, initiator, historical participant or CC;
56
+ - a versioned template reference;
57
+ - an allowlisted variable projection;
58
+ - channel priority/fallback;
59
+ - default detail target;
60
+ - whether to show Workflow Surface actions;
61
+ - whether a separate result message is required.
62
+
63
+ 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.
64
+
65
+ For a non-Workflow informational message, the public business contract accepts
66
+ only `title`, optional `summary`, recipients and a canonical navigation target.
67
+ Supply stable `eventId`, `idempotencyKey`, `messageKey` and monotonic
68
+ `sourceSequence`: duplicate events and idempotency keys replay the stored
69
+ message, while a later source sequence converges the same logical message.
70
+ Never forward a user token, application credential or global Notification Hub
71
+ permission from application code.
72
+
73
+ A signed event/date-trigger consumer uses
74
+ `OpenXiangdaBusinessNotificationService.sendFromEvent()`. The application
75
+ declares safe paths under event `data` for recipients, title and summary;
76
+ Notification Hub verifies the active durable delivery and resolves those paths
77
+ itself. The audit actor remains the application event consumer with eventId,
78
+ deliveryId and subscription code as initiating source. Missing or forged event
79
+ proof, arbitrary recipient values and user impersonation are rejected.
80
+
81
+ ## Detail navigation
82
+
83
+ 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.
84
+
85
+ The Notification Hub resolves desktop, mobile and channel deep links. The landing page always re-runs platform authentication and Data/AuthZ/Workflow authorization.
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
+
151
+ ## Interactive channel actions
152
+
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.
154
+
155
+ 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.
156
+
157
+ 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.
158
+
159
+ ## Event handling
160
+
161
+ Consume typed participant/task/instance facts with durable idempotency. Order Workflow message projection by `instanceId + instanceSequence`; never assume global event order. Terminal instance facts dominate stale participant facts and update every historical message for that instance.
162
+
163
+ For Native Data facts, the platform owns the capture plan under the exact Event
164
+ revision, Data revision, AuthZ revision and resource tuple selected by the
165
+ Native Head. Adding a snapshot field or extending RLS may create a new plan;
166
+ applications do not version it and must not remove the field to satisfy an old
167
+ plan. Historical replay uses the data and plan digest frozen in the original
168
+ fact, so it never gains fields from the current Head.
169
+
170
+ Every Workflow fact v2 carries the immutable instance identity envelope:
171
+ `workflowCode`, `definitionVersion`, `bindingVersion`, `instanceId`,
172
+ `generation`, `businessKey`, `instanceSequence`, `revision`, `dataRef`, and
173
+ `dataRevision`, plus `actor` and `cause`. Treat missing identity as an invalid
174
+ fact; never infer historical versions from the current Workflow Head.
175
+
176
+ 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.
177
+
178
+ ## Verification
179
+
180
+ Run the workspace generate/check/test/build gates plus real current-user positive and negative role-union cases. Acceptance must include 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.
@@ -0,0 +1,26 @@
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
+ - `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
+ - Use Vite, React Router, Refine Core and Ant Design. Do not add Umi, ProComponents or another admin shell.
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. 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.
7
+ - 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.
8
+ - 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.
9
+ - 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`.
10
+ - 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.
11
+ - Add NestJS only for a named business action that needs a cross-resource transaction, an invariant or an external side effect.
12
+ - 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.
13
+ - 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.
14
+ - Use the current logged-in user and the union of that user's application roles. 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.
15
+ - 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.
16
+ - 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.
17
+ - 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.
18
+ - 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.
19
+ - 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.
20
+ - 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.
21
+ - `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`.
22
+ - Derive role grants with `resourceCapabilityCodes(appCode, resourceCode)`. Declare current-user rows only with `currentUserDataPolicy(...)`; do not invent operators, values or alternate current-user spellings.
23
+ - 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.
24
+ - 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.
25
+ - 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.
26
+ - 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.