Commit 0eba3150 authored by drigle's avatar drigle

feat: add project subject schema manager skill

parent a562482f
---
name: subject-schema-manager
description: Sync Stream Subject definitions from the API into project documentation, remove unreferenced object sections, and apply confirmed object/field/configuration changes through the documented Subject API.
---
# Subject Schema Manager
Use this skill when the user asks to synchronize Subject/object documentation or asks the AI to create, update, or configure Stream Subjects and fields.
## Source of truth
- The live Subject API is authoritative for object and field structure.
- Read the repository's `SUBJECT_API.md`, `SUBJECT_CONFIGURATION.md`, `record-api.md`, and `subjectsDefinition.md` before acting. Prefer `src/services/record-api.md` and `src/services/subjectsDefinition.md` when the files exist there.
- `subjectsDefinition.md` is a versioned, human-readable snapshot generated from the API. It is not a runtime API client.
- Form resources are managed by `/form`; do not treat a `form` field as a Subject dependency.
## Modes
### `sync`
Use for requests to update object documentation or remove copied-project objects.
1. Discover Subject roots from static Subject constants and literal Subject API calls in the project's source. Prefer explicit roots supplied by the user or the script's `--roots` option when discovery is ambiguous.
2. Exchange the supplied ticket for a token using `POST /auth/login` with `{ "type": "ticket", "ticket": "..." }`. Never put the ticket or token in a file, generated Markdown, or normal command output.
3. Fetch each root with `GET /subject/{subject}`. Recursively follow every non-empty `field.settings.subject` on `reference`, `subject`, or other fields. Stop at cycles after documenting the object once.
4. Rebuild only the managed object-definition block in `subjectsDefinition.md`. The block must contain the root objects, every recursively referenced object, complete field properties, and dependency edges. Object sections outside the current dependency closure are removed from the managed block.
5. Do not mutate remote objects in this mode. A missing/ambiguous `settings.subject` is documented as unresolved; never invent a target object.
Run the deterministic helper from the project root:
```bash
node .agents/skills/subject-schema-manager/scripts/subject_schema_manager.mjs sync \
--project "$PWD" --ticket-stdin
```
Use `--check` for a read-only diff. The helper discovers the API base URL from `--base-url`, `STREAMS_API_URL`, or an absolute proxy target in `.umirc.ts`.
### `apply`
Use for requests that create objects, create/update fields, or update object configuration.
1. Inspect the API docs and produce an exact operation plan containing object names, field aliases, payloads, and dependency order. Include a concise human-readable summary and ask for explicit confirmation before any remote mutation.
2. After confirmation, write the plan as JSON outside the repository (for example `/tmp/subject-schema-plan.json`) and run the helper with `--confirmed --plan-file`. Pass the ticket via `SUBJECT_TICKET` or `--ticket-stdin`; never hardcode it.
3. The helper validates aliases and payloads, previews new objects with `POST /subject?preview=true`, then executes only the bounded operations in the plan:
- object create: `POST /subject`
- object update: `PUT /subject/{subject}`
- field create: `POST /subject/{subject}/field`
- field update: `PUT /subject/{subject}/field/{field}`
- field import/reorder when explicitly present in the plan
4. Remote object/field deletion is never implied by documentation cleanup. It requires explicit user intent and the helper's destructive-operation flag.
5. After successful mutations, re-fetch the complete dependency closure and run the same documentation sync using the authenticated token. Report successes and any partial-failure operation; there is no automatic rollback.
Run:
```bash
node .agents/skills/subject-schema-manager/scripts/subject_schema_manager.mjs apply \
--project "$PWD" --plan-file /tmp/subject-schema-plan.json \
--ticket-stdin --confirmed
```
The plan format is documented in [references/plan-schema.md](references/plan-schema.md). Do not use a generic arbitrary-URL request list; keep mutations within the documented Subject operations.
## Safety and documentation rules
- A plan confirmation authorizes only the listed operations. Do not add inferred fields, dependencies, or destructive operations after confirmation.
- Validate `select.settings.options`, `reference/subject settings.subject`, `required`, `multiple`, and `primary` against the API response or the confirmed plan.
- Use IDs for `reference` and `user` values, ISO 8601 for dates, and preserve `form` values as `{ form, data, settings? }`.
- If the API or local docs conflict, stop before mutation and show the exact conflict.
- If a business workflow or state rule changes, update the module's business-flow document separately; schema synchronization alone does not authorize workflow changes.
interface:
display_name: "Subject Schema Manager"
short_description: "Sync Subject docs and apply confirmed schema plans"
default_prompt: "Use $subject-schema-manager to sync current Subject definitions or apply a confirmed object/field configuration plan."
# Subject Mutation Plan
`apply` accepts a JSON file containing only the explicitly approved Subject API operations. Keep
the file outside the repository when it contains environment-specific values.
```json
{
"version": 1,
"operations": [
{
"op": "create_subject",
"subject": {
"name": "customers",
"title": "Customers",
"type": "normal",
"access": "public",
"fields": [],
"views": []
}
},
{
"op": "update_subject",
"subject": "orders",
"patch": {"title": "Orders", "history": true}
},
{
"op": "create_field",
"subject": "orders",
"field": {
"name": "customer",
"label": "Customer",
"type": "reference",
"required": false,
"multiple": false,
"settings": {"subject": "customers"}
}
},
{
"op": "update_field",
"subject": "orders",
"field": "status",
"patch": {
"label": "Order status",
"settings": {"options": [{"label": "Open", "value": "open"}]}
}
},
{
"op": "import_fields",
"subject": "orders",
"fields": [{"name": "amount", "label": "Amount", "type": "number"}]
},
{
"op": "reorder_fields",
"subject": "orders",
"fields": ["order_no", "customer", "status", "amount"]
}
]
}
```
Supported operation names are `create_subject`, `update_subject`, `create_field`, `update_field`,
`import_fields`, `reorder_fields`, `delete_subject`, and `delete_field`. Object and field aliases
must contain only letters, digits, `_`, or `-`. `update_subject.patch` and `update_field.patch`
must contain only the properties supported by the corresponding API endpoints in
`SUBJECT_API.md`; the helper rejects arbitrary URLs or methods.
Creation of an object is always sent to `POST /subject?preview=true` before any mutating request.
Deletion operations are accepted only with the command-line flag `--allow-remote-delete` in
addition to `--confirmed`. A deletion may set `force: true` and `confirm` where the API requires
it; `confirm` must exactly equal the alias being deleted. Documentation synchronization alone
never creates a deletion plan.
...@@ -21,12 +21,13 @@ ...@@ -21,12 +21,13 @@
- **常量**:使用全大写下划线 (SNAKE_CASE)。 - **常量**:使用全大写下划线 (SNAKE_CASE)。
- **技术栈偏好**: - **技术栈偏好**:
- 优先使用 React Hooks 和函数组件。 - 优先使用 React Hooks 和函数组件。
- 样式处理:**优先使用 Tailwind Plus / Catalyst UI Kit(见 `src/catalyst-ui-kit`)+ Tailwind CSS**。 - 样式处理:**优先使用 HeroUI / HeroUI Pro 官方组件与 compound API**,结合项目现有 Tailwind CSS 和主题 Token。
- 交互组件:**优先使用 Headless UI**(已在 Catalyst 组件内封装,除非缺组件才直接用 `@headlessui/react`)。 - 交互组件:**优先使用 HeroUI / HeroUI Pro**;弹窗、下拉、选择器等交互使用其官方可访问性实现,禁止直接引入 Headless UI。
- 组件选择优先级(从高到低): - 组件选择优先级(从高到低):
1. `src/catalyst-ui-kit/javascript/*`(Button/Input/Dialog/Table/Combobox/SidebarLayout/...) 1. `@heroui/react`(Button/Input/Autocomplete/DatePicker/Checkbox/Modal/Tabs 等基础组件)
2. 基于 Catalyst 样式规范的自定义组件(仅在 Kit 无现成组件时) 2. `@heroui-pro/react` 或已验证的 Pro 子路径(Sidebar/Navbar/DataGrid/DropZone/ActionBar/EmptyState/KPI 等)
3. 原生 HTML + 自写样式(尽量避免) 3. 基于 HeroUI compound API 的业务无关共享组合(仅在官方组件无法直接满足组合需求时)
4. 原生 HTML + 自写样式(仅在 HeroUI/HeroUI Pro 均无对应能力时,且需保持项目主题和可访问性)
## 4. 接口请求规范 ## 4. 接口请求规范
...@@ -39,12 +40,12 @@ ...@@ -39,12 +40,12 @@
- 如果 `record-api.md` 或 `subjectsDefinition.md` 中的定义不清晰,请务必询问我,不要随意猜测字段名。 - 如果 `record-api.md` 或 `subjectsDefinition.md` 中的定义不清晰,请务必询问我,不要随意猜测字段名。
- 每次生成代码后,简要说明你引用了哪个 API 和哪些数据对象。 - 每次生成代码后,简要说明你引用了哪个 API 和哪些数据对象。
## 6. UI 规范(Tailwind Plus / Headless UI 强制约束) ## 6. UI 规范(HeroUI / HeroUI Pro 强制约束)
- **全项目 UI 统一**:默认使用 `src/catalyst-ui-kit` 中的 Catalyst 组件与样式体系,避免引入其它 UI 框架。 - **全项目 UI 统一**:默认使用 `@heroui/react` 与 `@heroui-pro/react` 的官方组件和样式体系,禁止使用 `src/catalyst-ui-kit` 或引入其它 UI 框架。
- **Headless UI 优先**:弹窗/下拉/选择器等交互优先用 Headless UI(直接或通过 Catalyst 组件)。 - **官方交互优先**:弹窗、下拉、选择器、侧栏等交互必须使用 HeroUI/HeroUI Pro 官方 compound API,保留 focus trap、Escape 关闭和键盘导航等可访问性行为。
- **一致性**:表单控件优先用 Catalyst 的 `Input/Select/Checkbox/Textarea/Fieldset`,表格用 `Table`,对话框用 `Dialog`。 - **一致性**:基础表单控件使用 HeroUI `Input/Autocomplete/DatePicker/Checkbox/Textarea`,表格使用 HeroUI Pro `DataGrid`,对话框使用 HeroUI `Modal` / `AlertDialog`,不得以旧组件或自制包装层替代。
- **可访问性**:移动端侧栏/弹层必须使用 Headless UI 的 Dialog 等可访问性组件(focus trap / esc 关闭等)。 - **事件与组合约定**:按钮和交互控件使用 `onPress`;复合组件使用官方点号结构(例如 `Modal.Root`、`Tabs.List`),不得复刻旧组件 props、DOM event 或样式钩子。
# AI 开发执行规范 # AI 开发执行规范
...@@ -54,12 +55,12 @@ ...@@ -54,12 +55,12 @@
| 字段类型 (Type) | UI 组件 (Component) | 交互逻辑 / 备注 | | 字段类型 (Type) | UI 组件 (Component) | 交互逻辑 / 备注 |
| :-- | :-- | :-- | | :-- | :-- | :-- |
| **文本 (String)** | Catalyst `Input` | 标准文本输入 | | **文本 (String)** | HeroUI `Input` | 标准文本输入 |
| **引用 (Reference)** | `ReferenceSearchSelect`(Headless UI Combobox) | 必须通过 API 获取列表,选中后仅存储 `_id` | | **引用 (Reference)** | `ReferenceSearchSelect`(HeroUI `Autocomplete`) | 必须通过 API 获取列表,选中后仅存储 `_id` |
| **数字 (Number)** | Catalyst `Input`(`type="number"`) | 仅允许输入数字 | | **数字 (Number)** | HeroUI `Input`(`type="number"`) | 仅允许输入数字 |
| **日期 (Date)** | Catalyst `Input`(`type="date"` / `datetime-local`) | 提交时格式化为 `ISO 8601` 字符串 | | **日期 (Date)** | HeroUI `DatePicker` / `DateField` | 提交时格式化为 `ISO 8601` 字符串 |
| **布尔 (Boolean)** | Catalyst `Checkbox` | 映射为 true/false | | **布尔 (Boolean)** | HeroUI `Checkbox` | 映射为 true/false |
| **JSON** | Catalyst `Textarea` | 需要包含 JSON 校验逻辑 | | **JSON** | HeroUI `Textarea` | 需要包含 JSON 校验逻辑 |
| **子对象 (Subject)** | **动态表单递归** | **核心逻辑:** 必须先调用 `loadSubject(subjectName)` 接口读取该子对象的字段定义,然后递归应用本映射表生成子表单。 | | **子对象 (Subject)** | **动态表单递归** | **核心逻辑:** 必须先调用 `loadSubject(subjectName)` 接口读取该子对象的字段定义,然后递归应用本映射表生成子表单。 |
--- ---
...@@ -81,7 +82,7 @@ AI 在编写 API 调用或数据转换逻辑时,必须遵守: ...@@ -81,7 +82,7 @@ AI 在编写 API 调用或数据转换逻辑时,必须遵守:
1. **Step 1**: 查找 `subjectsDefinition.md` 获取该 Subject 的基础字段。 1. **Step 1**: 查找 `subjectsDefinition.md` 获取该 Subject 的基础字段。
2. **Step 2**: 检查字段类型。如果遇到 `Subject` 类型,**自动生成**一段调用 `loadSubject` 的代码以获取深层结构。 2. **Step 2**: 检查字段类型。如果遇到 `Subject` 类型,**自动生成**一段调用 `loadSubject` 的代码以获取深层结构。
3. **Step 3**: 参照上表(第 1 节)选择 tailwind (或你指定的 UI 库) 的组件。 3. **Step 3**: 参照上表(第 1 节)选择 HeroUI / HeroUI Pro 组件,并遵守官方 compound API。
4. **Step 4**: 自动生成 `onChange` 处理函数,确保数据实时同步到 `metadata` 对象的对应路径下。 4. **Step 4**: 自动生成 `onChange` 处理函数,确保数据实时同步到 `metadata` 对象的对应路径下。
--- ---
...@@ -106,6 +107,12 @@ AI 在编写 API 调用或数据转换逻辑时,必须遵守: ...@@ -106,6 +107,12 @@ AI 在编写 API 调用或数据转换逻辑时,必须遵守:
- 项目管理模块 - 请阅读 `/src/pages/project_manager_xy/project-management.business-flow.md` - 项目管理模块 - 请阅读 `/src/pages/project_manager_xy/project-management.business-flow.md`
## Subject Schema Skill
- 项目内置 Skill 位于 `.agents/skills/subject-schema-manager/`,用于同步 `subjectsDefinition.md` 及执行已确认的 Subject 对象、字段和配置计划。
- 需要同步文档时运行 `node .agents/skills/subject-schema-manager/scripts/subject_schema_manager.mjs sync --project "$PWD" --ticket-stdin`。
- 需要修改线上配置时,必须先展示并确认 JSON 计划,再使用 `apply --plan-file ... --confirmed`;ticket 只允许通过 `SUBJECT_TICKET` 或标准输入提供,不得写入仓库。
## 获取当前登录用户信息 ## 获取当前登录用户信息
可使用 src/hooks/user.jsx 中的 hook useUserInfo 数据结构如下 { "username": "yang_admin", "password_changed": true, "token_expires_at": null, "\_id": "69b12a47e92b1de77c94a9f5", "display_name": "杨康", "type": "admin", "avatar": null, "profile": {}, "groups": [ { "\_id": "649e5a55ad1c1038911baabd", "name": "default", "display_name": "默认用户组", "users": [ { "_id": "649e5a55ad1c1038911baac0", "username": "system", "display_name": "系统" } ], "settings": {} } ], "applications": [ { "name": "fund_audit", "title": "数智报告平台", "groups": [ "default" ], "roles": [ "成员" ], "permissions": [] }, { "name": "aml_tables", "title": "反洗钱数据表", "groups": [ "default" ], "roles": [ "管理" ], "permissions": [] } ] } 可使用 src/hooks/user.jsx 中的 hook useUserInfo 数据结构如下 { "username": "yang_admin", "password_changed": true, "token_expires_at": null, "\_id": "69b12a47e92b1de77c94a9f5", "display_name": "杨康", "type": "admin", "avatar": null, "profile": {}, "groups": [ { "\_id": "649e5a55ad1c1038911baabd", "name": "default", "display_name": "默认用户组", "users": [ { "_id": "649e5a55ad1c1038911baac0", "username": "system", "display_name": "系统" } ], "settings": {} } ], "applications": [ { "name": "fund_audit", "title": "数智报告平台", "groups": [ "default" ], "roles": [ "成员" ], "permissions": [] }, { "name": "aml_tables", "title": "反洗钱数据表", "groups": [ "default" ], "roles": [ "管理" ], "permissions": [] } ] }
This diff is collapsed.
# Object Configuration Migration
对象、字段、视图和 ticket 换 token 的完整 HTTP API 参考见 [SUBJECT_API.md](./SUBJECT_API.md)。本文保留配置包迁移的详细说明。
The object configuration package moves metadata between Streams environments. It does not move records, database IDs, object members, tags, categories, connector settings, credentials, or existing external database tables.
## Workflow
1. Select one or more normal, embedded, or external objects in Admin > Object Management and choose `Export Configuration`.
2. Referenced objects are included automatically. The selected objects are listed in `selected_subjects`; dependency objects follow them in `subjects`.
3. In the target environment choose `Import Configuration` and select the JSON file.
4. Review the precheck. Only a package with no blocking issues can be applied.
5. The import uses `upsert`: matching objects and fields are updated, new ones are created, and target-only fields and views are preserved.
For an external object, create and enable a DM8 connector in the target environment before import. Its stable `connector.name` must match the alias exported in `external.connector`. A new external object provisions a new managed table using the `dm_<subject.name>` convention; import never adopts, binds to, or overwrites a pre-existing physical table.
## Stable identifiers
The following identifiers are portable business codes and must not be replaced with database IDs:
- object: `subject.name`
- field: `field.name`
- select option: `field.settings.options[].value`
- reference target: `field.settings.subject`
- external connector: `subject.external.connector` (`connector.name`)
For example:
```json
{
"kind": "streams.subject-configuration",
"version": 1,
"selected_subjects": ["orders"],
"include_dependencies": true,
"subjects": [
{
"name": "orders",
"title": "Orders",
"type": "normal",
"fields": [
{
"name": "status",
"label": "Status",
"type": "select",
"settings": {
"options": [
{"label": "Open", "value": "open"}
]
}
},
{
"name": "customer",
"label": "Customer",
"type": "reference",
"settings": {"subject": "customers"}
}
],
"views": []
},
{
"name": "customers",
"title": "Customers",
"type": "normal",
"fields": [],
"views": []
}
]
}
```
An external object only exports its connector alias:
```json
{
"name": "external_orders",
"title": "External Orders",
"type": "external",
"external": {
"connector": "business_dm"
},
"fields": [],
"views": []
}
```
The package never contains the connector host, account, password, settings, runtime state, physical table metadata, or records.
## API
- `POST /subject/configuration/export` with `{subjects: ["orders"], include_dependencies: true}`
- `POST /subject/configuration/import/preview` with `{mode: "upsert", package: {...}}`
- `POST /subject/configuration/import` with `{mode: "upsert", package: {...}}`
The import order is normal object shells, complete new external objects, object properties, normal fields, reference fields, and views. A new external object is created once with its complete field definition so the managed table is provisioned once. This also allows circular references between objects to be imported after referenced object names exist.
External-object precheck blocks import when:
- `external.connector` is missing or invalid;
- the same-alias connector does not exist, is inactive, or is not DM8-compatible;
- the target table for a new external object already exists;
- an existing external object's connector alias differs from the package;
- an existing external object is not in the `ready` state;
- an external field change is not supported by the managed-table schema.
## Deliberate limitations in V1
- Records are never included.
- Connector settings and credentials are never included; target connectors are resolved by alias.
- Existing physical tables are never adopted or overwritten by configuration import.
- Members, tags, and categories are reported as warnings and are not imported.
- Upsert never deletes fields or views that only exist in the target environment.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment