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.
- package/README.md +10 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +35 -0
- package/dist/bin.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +309 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/skill-installer.d.ts +8 -0
- package/dist/internal/skill-installer.d.ts.map +1 -0
- package/dist/internal/skill-installer.js +51 -0
- package/dist/internal/skill-installer.js.map +1 -0
- package/package.json +36 -0
- package/skills/manifest.json +10 -0
- package/skills/openxiangda-v2/SKILL.md +67 -0
- package/skills/openxiangda-v2/agents/openai.yaml +4 -0
- package/skills/openxiangda-v2/references/appspec.md +67 -0
- package/skills/openxiangda-v2/references/architecture.md +7 -0
- package/skills/openxiangda-v2/references/backend.md +268 -0
- package/skills/openxiangda-v2/references/commands.md +21 -0
- package/skills/openxiangda-v2/references/data-authz.md +270 -0
- package/skills/openxiangda-v2/references/delivery.md +42 -0
- package/skills/openxiangda-v2/references/discovery.md +15 -0
- package/skills/openxiangda-v2/references/frontend.md +131 -0
- package/skills/openxiangda-v2/references/testing.md +66 -0
- package/skills/openxiangda-v2/references/workflow-events.md +180 -0
- package/skills/openxiangda-v2/references/workspace.md +26 -0
|
@@ -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.
|