@teamix-evo/skills 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +7 -3
  2. package/manifest.json +63 -46
  3. package/package.json +3 -3
  4. package/src/teamix-evo-code-opentrek/SKILL.md +94 -0
  5. package/{skills/teamix-evo-coding-conventions → src/teamix-evo-code-opentrek}/api-layering.md +8 -5
  6. package/{skills/teamix-evo-coding-conventions → src/teamix-evo-code-opentrek}/checklist.md +4 -2
  7. package/{skills/teamix-evo-coding-conventions → src/teamix-evo-code-opentrek}/error-and-loading.md +38 -25
  8. package/src/teamix-evo-code-opentrek/file-structure.md +282 -0
  9. package/{skills/teamix-evo-coding-conventions → src/teamix-evo-code-opentrek}/forms-and-validation.md +14 -12
  10. package/{skills/teamix-evo-coding-conventions → src/teamix-evo-code-opentrek}/reuse-first.md +27 -17
  11. package/{skills/teamix-evo-coding-conventions → src/teamix-evo-code-opentrek}/routing-and-codesplit.md +23 -21
  12. package/{skills/teamix-evo-coding-conventions → src/teamix-evo-code-opentrek}/testing.md +32 -28
  13. package/src/teamix-evo-code-uni-manager/SKILL.md +97 -0
  14. package/src/teamix-evo-code-uni-manager/api-layering.md +372 -0
  15. package/src/teamix-evo-code-uni-manager/checklist.md +195 -0
  16. package/src/teamix-evo-code-uni-manager/error-and-loading.md +391 -0
  17. package/src/teamix-evo-code-uni-manager/file-structure.md +341 -0
  18. package/src/teamix-evo-code-uni-manager/forms-and-validation.md +461 -0
  19. package/src/teamix-evo-code-uni-manager/reuse-first.md +190 -0
  20. package/src/teamix-evo-code-uni-manager/routing-and-codesplit.md +452 -0
  21. package/src/teamix-evo-code-uni-manager/testing.md +398 -0
  22. package/src/teamix-evo-design-opentrek/SKILL.md +64 -0
  23. package/src/teamix-evo-design-opentrek/boundaries.md +533 -0
  24. package/src/teamix-evo-design-opentrek/brand.md +154 -0
  25. package/src/teamix-evo-design-opentrek/checklist.md +85 -0
  26. package/src/teamix-evo-design-opentrek/components.md +294 -0
  27. package/src/teamix-evo-design-opentrek/flows.md +51 -0
  28. package/src/teamix-evo-design-opentrek/foundations.md +274 -0
  29. package/src/teamix-evo-design-opentrek/generation-flow.md +243 -0
  30. package/src/teamix-evo-design-opentrek/patterns/color-mapping.md +96 -0
  31. package/src/teamix-evo-design-opentrek/patterns/dashboard.md +33 -0
  32. package/src/teamix-evo-design-opentrek/patterns/detail-page.md +203 -0
  33. package/src/teamix-evo-design-opentrek/patterns/form-page.md +292 -0
  34. package/src/teamix-evo-design-opentrek/patterns/list-page.md +367 -0
  35. package/src/teamix-evo-design-opentrek/patterns/page-types.md +159 -0
  36. package/src/teamix-evo-design-opentrek/patterns/sidebar.md +122 -0
  37. package/src/teamix-evo-design-opentrek/philosophy.md +98 -0
  38. package/src/teamix-evo-design-opentrek/rules/README.md +39 -0
  39. package/src/teamix-evo-design-opentrek/rules/boundaries.rules.json +391 -0
  40. package/src/teamix-evo-design-uni-manager/SKILL.md +64 -0
  41. package/src/teamix-evo-design-uni-manager/boundaries.md +567 -0
  42. package/src/teamix-evo-design-uni-manager/brand.md +202 -0
  43. package/src/teamix-evo-design-uni-manager/checklist.md +115 -0
  44. package/src/teamix-evo-design-uni-manager/components.md +257 -0
  45. package/src/teamix-evo-design-uni-manager/flows.md +63 -0
  46. package/src/teamix-evo-design-uni-manager/foundations.md +261 -0
  47. package/src/teamix-evo-design-uni-manager/generation-flow.md +230 -0
  48. package/src/teamix-evo-design-uni-manager/patterns/dashboard.md +97 -0
  49. package/src/teamix-evo-design-uni-manager/patterns/detail-page.md +253 -0
  50. package/src/teamix-evo-design-uni-manager/patterns/form-page.md +366 -0
  51. package/src/teamix-evo-design-uni-manager/patterns/list-page.md +389 -0
  52. package/src/teamix-evo-design-uni-manager/patterns/page-types.md +167 -0
  53. package/src/teamix-evo-design-uni-manager/philosophy.md +108 -0
  54. package/src/teamix-evo-design-uni-manager/rules/README.md +49 -0
  55. package/src/teamix-evo-design-uni-manager/rules/boundaries.rules.json +418 -0
  56. package/src/teamix-evo-manage/SKILL.md +289 -0
  57. package/skills/teamix-evo-coding-conventions/SKILL.md +0 -92
  58. package/skills/teamix-evo-coding-conventions/file-structure.md +0 -273
  59. package/skills/teamix-evo-design-rules/SKILL.md +0 -86
  60. package/skills/teamix-evo-design-rules/boundaries.md +0 -89
  61. package/skills/teamix-evo-design-rules/checklist.md +0 -108
  62. package/skills/teamix-evo-design-rules/generation-flow.md +0 -142
  63. package/skills/teamix-evo-design-rules/prompts/page-design.md +0 -148
  64. package/skills/teamix-evo-design-rules-opentrek/SKILL.md +0 -48
  65. package/skills/teamix-evo-design-rules-opentrek/brand-rules.md +0 -74
  66. package/skills/teamix-evo-design-rules-uni-manager/SKILL.md +0 -51
  67. package/skills/teamix-evo-design-rules-uni-manager/ai-scenarios.md +0 -51
  68. package/skills/teamix-evo-design-rules-uni-manager/command-center.md +0 -108
  69. package/skills/teamix-evo-design-rules-uni-manager/danger-ops.md +0 -87
  70. package/skills/teamix-evo-manage/SKILL.md +0 -178
  71. package/skills/teamix-evo-ui-upgrade/SKILL.md +0 -75
@@ -0,0 +1,398 @@
1
+ # 测试规范(uni-manager)
2
+
3
+ > ⚠️ **Prerequisites**: 本文件是 [SKILL.md](./SKILL.md) Step 7 的读取项。纯函数和 zod schema 必测,其余按关键路径覆盖。
4
+
5
+ > **核心约定**:`vitest` + `@testing-library/react` + `msw`,文件就近 `*.test.ts(x)`。**不追求覆盖率数字**,追求"关键路径必有测试 + 纯函数全测"。**uni-manager 额外要求 msw handler 校验 X-Tenant-Id / X-Region-Id / X-Cloud-Provider 请求头。**
6
+
7
+ ---
8
+
9
+ ## 选型(业界主流)
10
+
11
+ | 类型 | 工具 | 理由 |
12
+ | ----------- | -------------------------------------------------------- | ------------------------------------------------ |
13
+ | 测试 runner | `vitest` | 与 Vite 原生集成,ESM 友好,API 与 Jest 兼容 |
14
+ | 组件测试 | `@testing-library/react` + `@testing-library/user-event` | 测行为不测实现,业界事实标准(Kent C. Dodds) |
15
+ | API mock | `msw`(Mock Service Worker) | 拦截真实 fetch / axios,测试与生产共用 service 层 |
16
+ | 断言 | `vitest` 内置 `expect`(兼容 Jest) | 无需 chai |
17
+ | E2E(可选) | `playwright` | 关键流程冒烟,**不在本 skill 范围** |
18
+
19
+ **不要混用**:已经选 vitest 就不要再加 jest;已经用 msw 就不要再自己 mock axios。
20
+
21
+ ---
22
+
23
+ ## §1 · 文件位置
24
+
25
+ 测试文件**就近**放在被测文件同目录,`<name>.test.ts(x)`:
26
+
27
+ ```
28
+ src/
29
+ ├── utils/
30
+ │ ├── format.ts
31
+ │ └── format.test.ts # ← 紧邻
32
+ ├── services/
33
+ │ ├── instance.ts
34
+ │ └── instance.test.ts
35
+ ├── hooks/
36
+ │ ├── useInstanceList.ts
37
+ │ └── useInstanceList.test.ts
38
+ └── components/
39
+ ├── InstanceStatusBadge.tsx
40
+ └── InstanceStatusBadge.test.tsx
41
+ ```
42
+
43
+ **不要**集中放在 `tests/` / `__tests__/` 顶层 —— 业界共识已偏向就近,便于维护与移动。
44
+
45
+ 测试辅助资源(fixtures、mock handler)放 `src/test/`:
46
+
47
+ ```
48
+ src/test/
49
+ ├── setup.ts # vitest setup(jest-dom、msw server、active-context 重置)
50
+ ├── handlers.ts # msw handler 集合(必须断言 X-Tenant-Id 等 header)
51
+ ├── render.tsx # 自定义 render(包 Provider:QueryClient + Tenant + Region)
52
+ └── fixtures/
53
+ ├── instance.ts # 测试数据工厂
54
+ └── tenant.ts # 租户工厂
55
+ ```
56
+
57
+ ---
58
+
59
+ ## §2 · 应该写什么测试(优先级)
60
+
61
+ 按 ROI 排序:
62
+
63
+ | 优先级 | 类型 | 例子 | 必测? |
64
+ | ------ | --------------------------------------- | ---------------------------------------------- | -------------- |
65
+ | P0 | 纯函数(`src/utils/`、`src/services/`) | `formatMoney`、`isValidInstance` | ✅ **必测** |
66
+ | P0 | zod schema | `CreateInstanceInputSchema.parse(...)` | ✅ **必测** |
67
+ | P0 | 关键业务路径(创建实例 / 释放 / 切租户) | E2E-style 组件测 | ✅ **必测** |
68
+ | P0 | **interceptor / active-context 切换** | 切租户后请求带新 X-Tenant-Id,旧缓存 invalidate | ✅ **必测** |
69
+ | P0 | **danger 流程(useDangerConfirm)** | 输入正确名才 enable 按钮、关闭后状态重置 | ✅ **必测** |
70
+ | P1 | Hook(数据 hook + 自定义 UI hook) | `useInstanceList`、`useDebounce` | 推荐 |
71
+ | P1 | 复用业务组件(`src/components/`) | `CloudBadge` 各 cloud 渲染 | 推荐 |
72
+ | P2 | 页面组件 | 用 RTL 渲染 + msw mock | 可选 |
73
+ | ❌ | ui 包源码 | 已在 `@teamix-evo/ui` 包内测过 | **不要重复测** |
74
+ | ❌ | biz-ui/uni-manager 包源码(如 um-topbar) | 已在 biz-ui 包内测过 | **不要重复测** |
75
+ | ❌ | 三方库 | `react-router`、`react-query` | **不要测** |
76
+
77
+ **红线**:不要为了凑覆盖率数字测 `Button` 能不能渲染、`React.useState` 能不能用。
78
+
79
+ ---
80
+
81
+ ## §3 · 标准测试模式
82
+
83
+ ### 纯函数
84
+
85
+ ```ts
86
+ // src/utils/format.test.ts
87
+ import { describe, it, expect } from 'vitest';
88
+ import { formatCloudProvider } from './cloud';
89
+
90
+ describe('formatCloudProvider', () => {
91
+ it('aws → Amazon Web Services', () => {
92
+ expect(formatCloudProvider('aws')).toBe('Amazon Web Services');
93
+ });
94
+ it('未知 cloud 回退到原值', () => {
95
+ expect(formatCloudProvider('unknown' as never)).toBe('unknown');
96
+ });
97
+ });
98
+ ```
99
+
100
+ 约定:
101
+
102
+ - 一个 `describe` 一个函数
103
+ - 一个 `it` 一个行为(不堆 5 个 expect 测无关行为)
104
+ - 测试名是**行为描述**,不是函数名
105
+
106
+ ### Service 函数(用 msw,**必须校验 tenant/region header**)
107
+
108
+ ```ts
109
+ // src/test/handlers.ts
110
+ import { http, HttpResponse } from 'msw';
111
+
112
+ // uni-manager 强约束:每个 handler 都要断言 tenant header
113
+ function assertTenantHeader(request: Request): string {
114
+ const tenantId = request.headers.get('X-Tenant-Id');
115
+ if (!tenantId) {
116
+ throw new Error('Missing X-Tenant-Id header — interceptor 未注入或被绕过');
117
+ }
118
+ return tenantId;
119
+ }
120
+
121
+ export const handlers = [
122
+ http.get('/api/instances', ({ request }) => {
123
+ const tenantId = assertTenantHeader(request);
124
+ const regionId = request.headers.get('X-Region-Id');
125
+ return HttpResponse.json({
126
+ list: [{ id: 'i-1', tenantId, regionId, status: 'running' }],
127
+ });
128
+ }),
129
+ http.post('/api/instances', async ({ request }) => {
130
+ assertTenantHeader(request);
131
+ const body = (await request.json()) as Record<string, unknown>;
132
+ return HttpResponse.json({ id: 'i-new', ...body });
133
+ }),
134
+ // 跨云聚合:cloud header 必须存在
135
+ http.get('/api/cloud/instances', ({ request }) => {
136
+ const cloud = request.headers.get('X-Cloud-Provider');
137
+ if (!cloud) throw new Error('Missing X-Cloud-Provider header');
138
+ return HttpResponse.json({ list: [], cloud });
139
+ }),
140
+ ];
141
+ ```
142
+
143
+ ```ts
144
+ // src/test/setup.ts
145
+ import { setupServer } from 'msw/node';
146
+ import { afterAll, afterEach, beforeAll, beforeEach } from 'vitest';
147
+ import '@testing-library/jest-dom/vitest';
148
+ import { handlers } from './handlers';
149
+ import { setActiveTenant, setActiveRegion } from '@/lib/active-context';
150
+
151
+ const server = setupServer(...handlers);
152
+ beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
153
+ afterEach(() => server.resetHandlers());
154
+ afterAll(() => server.close());
155
+
156
+ // uni-manager 必须:每个 test 前重置 active-context,避免上一 test 污染
157
+ beforeEach(() => {
158
+ setActiveTenant({ id: 't-default', name: 'Default Tenant' });
159
+ setActiveRegion({ id: 'cn-hangzhou', name: '华东1' });
160
+ });
161
+ ```
162
+
163
+ ```ts
164
+ // src/services/instance.test.ts
165
+ import { describe, it, expect } from 'vitest';
166
+ import { listInstances } from './instance';
167
+ import { setActiveTenant } from '@/lib/active-context';
168
+
169
+ describe('listInstances', () => {
170
+ it('返回实例列表(自动带当前 tenant header)', async () => {
171
+ const instances = await listInstances();
172
+ expect(instances).toHaveLength(1);
173
+ expect(instances[0].tenantId).toBe('t-default');
174
+ });
175
+
176
+ it('切换 active tenant 后,后续请求带新 tenantId', async () => {
177
+ setActiveTenant({ id: 't-other', name: 'Other Tenant' });
178
+ const instances = await listInstances();
179
+ expect(instances[0].tenantId).toBe('t-other');
180
+ });
181
+ });
182
+ ```
183
+
184
+ ### Hook(包 Provider 树)
185
+
186
+ ```tsx
187
+ // src/test/render.tsx
188
+ import { render, renderHook, type RenderOptions } from '@testing-library/react';
189
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
190
+ import { MemoryRouter } from 'react-router-dom';
191
+ import { TenantProvider } from '@/contexts/TenantContext';
192
+ import { RegionProvider } from '@/contexts/RegionContext';
193
+
194
+ function createWrapper(route = '/') {
195
+ return function Wrapper({ children }: { children: React.ReactNode }) {
196
+ const qc = new QueryClient({
197
+ defaultOptions: { queries: { retry: false } },
198
+ });
199
+ return (
200
+ <QueryClientProvider client={qc}>
201
+ <TenantProvider>
202
+ <RegionProvider>
203
+ <MemoryRouter initialEntries={[route]}>{children}</MemoryRouter>
204
+ </RegionProvider>
205
+ </TenantProvider>
206
+ </QueryClientProvider>
207
+ );
208
+ };
209
+ }
210
+
211
+ export function renderWithProviders(
212
+ ui: React.ReactElement,
213
+ { route = '/', ...options }: RenderOptions & { route?: string } = {},
214
+ ) {
215
+ return render(ui, { wrapper: createWrapper(route), ...options });
216
+ }
217
+
218
+ export function renderHookWithProviders<R, P>(
219
+ hook: (props: P) => R,
220
+ { route = '/' }: { route?: string } = {},
221
+ ) {
222
+ return renderHook(hook, { wrapper: createWrapper(route) });
223
+ }
224
+ ```
225
+
226
+ ```tsx
227
+ // src/hooks/useInstanceList.test.tsx
228
+ import { waitFor } from '@testing-library/react';
229
+ import { renderHookWithProviders } from '@/test/render';
230
+ import { useInstanceList } from './useInstanceList';
231
+
232
+ it('加载实例列表(queryKey 含 tenantId)', async () => {
233
+ const { result } = renderHookWithProviders(() =>
234
+ useInstanceList({ status: 'all' }),
235
+ );
236
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
237
+ expect(result.current.data).toHaveLength(1);
238
+ });
239
+ ```
240
+
241
+ ### 组件(testing-library 范式:测行为不测实现)
242
+
243
+ ```tsx
244
+ // src/components/InstanceStatusBadge.test.tsx
245
+ import { render, screen } from '@testing-library/react';
246
+ import { InstanceStatusBadge } from './InstanceStatusBadge';
247
+
248
+ it('running 状态显示"运行中"', () => {
249
+ render(<InstanceStatusBadge status="running" />);
250
+ expect(screen.getByText('运行中')).toBeInTheDocument();
251
+ });
252
+
253
+ it('terminated 状态有 destructive 视觉', () => {
254
+ render(<InstanceStatusBadge status="terminated" />);
255
+ expect(screen.getByText('已销毁')).toHaveClass('bg-destructive');
256
+ });
257
+ ```
258
+
259
+ ### 用户交互(`user-event`)
260
+
261
+ ```tsx
262
+ import userEvent from '@testing-library/user-event';
263
+ import { renderWithProviders } from '@/test/render';
264
+
265
+ it('释放实例:必须输入完整名称才能确认', async () => {
266
+ const onConfirm = vi.fn();
267
+ renderWithProviders(
268
+ <DangerConfirmDialog
269
+ open
270
+ title="释放实例 i-12345"
271
+ resourceName="i-12345"
272
+ onConfirm={onConfirm}
273
+ onClose={() => {}}
274
+ />,
275
+ );
276
+
277
+ const confirmBtn = screen.getByRole('button', { name: '确认释放' });
278
+ expect(confirmBtn).toBeDisabled();
279
+
280
+ await userEvent.type(screen.getByLabelText(/输入实例 ID 确认/), 'i-1234');
281
+ expect(confirmBtn).toBeDisabled(); // 不完全匹配
282
+
283
+ await userEvent.type(screen.getByLabelText(/输入实例 ID 确认/), '5');
284
+ expect(confirmBtn).toBeEnabled();
285
+
286
+ await userEvent.click(confirmBtn);
287
+ expect(onConfirm).toHaveBeenCalledOnce();
288
+ });
289
+ ```
290
+
291
+ **不要**用 `fireEvent` —— 它不模拟真实键盘 / 鼠标事件序列,`user-event` 才贴近真实用户。
292
+
293
+ ---
294
+
295
+ ## §4 · 关键集成测试:切租户 → invalidate → 新数据
296
+
297
+ uni-manager 的核心交互之一是切换租户,**必须**有集成测试覆盖:
298
+
299
+ ```tsx
300
+ // src/contexts/TenantContext.test.tsx
301
+ import { renderWithProviders } from '@/test/render';
302
+ import userEvent from '@testing-library/user-event';
303
+ import { screen, waitFor } from '@testing-library/react';
304
+
305
+ it('切换租户:invalidate 旧 query + 新请求带新 tenantId', async () => {
306
+ renderWithProviders(<TenantSwitcherTestHarness />);
307
+
308
+ // 初始:t-default 的实例
309
+ await waitFor(() => screen.getByText(/i-1.*t-default/));
310
+
311
+ // 切换到 t-other
312
+ await userEvent.click(screen.getByRole('button', { name: '切换到 t-other' }));
313
+
314
+ // 应该看到新 tenant 的实例(msw 根据 header 返回不同数据)
315
+ await waitFor(() => screen.getByText(/i-1.*t-other/));
316
+ });
317
+ ```
318
+
319
+ ---
320
+
321
+ ## §5 · 反模式速查
322
+
323
+ | 反模式 | 为什么禁 | 应该 |
324
+ | ---------------------------------------- | --------------------------------- | ----------------------------------------------- |
325
+ | 测 `setState` 是否被调用 | 测了实现,重构即崩 | 测渲染结果 / 用户行为 |
326
+ | `getByTestId` 当默认查询 | data-testid 跟可访问性绑不上 | `getByRole` / `getByLabelText` 优先 |
327
+ | 一个 it 里 expect 10 次无关断言 | 失败定位难 | 拆多个 it |
328
+ | mock 整个 hook(`vi.mock('@/hooks/...')`) | 失去集成价值 | msw mock 后端,hook 真跑 |
329
+ | 自己 mock fetch / axios | 与生产代码路径不一致 | 用 msw |
330
+ | msw handler 不校验 X-Tenant-Id | interceptor 失效不会被发现 | 每个 handler `assertTenantHeader(request)` |
331
+ | 测试间不重置 active-context | 上一 test 污染下一 test 的 tenant | `beforeEach` 重置 |
332
+ | 测试里写 `setTimeout(..., 1000)` 等异步 | 慢且不稳定 | `await waitFor` / `findBy*` |
333
+ | 全局 mock console.error 静默 | 错过真实告警 | 让测试输出 console.error,但 setup 里 fail on it |
334
+ | 追求 80% 覆盖率刷数字 | 测了没价值的代码 | 按"优先级表"测,不看覆盖率盲冲 |
335
+
336
+ ---
337
+
338
+ ## §6 · 装机指引
339
+
340
+ ```bash
341
+ pnpm add -D vitest @vitest/ui @testing-library/react @testing-library/user-event @testing-library/jest-dom jsdom msw
342
+ ```
343
+
344
+ ```ts
345
+ // vite.config.ts
346
+ import { defineConfig } from 'vitest/config';
347
+
348
+ export default defineConfig({
349
+ test: {
350
+ environment: 'jsdom',
351
+ setupFiles: ['./src/test/setup.ts'],
352
+ globals: true, // 可选:免 import describe / it / expect
353
+ },
354
+ });
355
+ ```
356
+
357
+ ```json
358
+ // package.json scripts
359
+ {
360
+ "test": "vitest",
361
+ "test:run": "vitest run",
362
+ "test:ui": "vitest --ui"
363
+ }
364
+ ```
365
+
366
+ `msw` 还需要初始化 worker(用于 dev 环境)或 server(node):
367
+
368
+ ```bash
369
+ pnpm exec msw init public/ --save # 浏览器 worker
370
+ ```
371
+
372
+ ---
373
+
374
+ ## §7 · 何时跑测试
375
+
376
+ - **本地开发**:`pnpm test`(watch 模式),改文件即跑相邻 test
377
+ - **commit 前**:pre-commit hook 跑改动文件的 test(可选,慢就关)
378
+ - **CI**:`pnpm test:run`(必须,跑全量)
379
+ - **PR**:CI 红 → 不合并
380
+
381
+ ---
382
+
383
+ ## AI 必须输出的测试日志
384
+
385
+ 新增 / 修改业务代码时,AI 必须在响应里说明测试覆盖情况:
386
+
387
+ ```
388
+ ## 测试覆盖
389
+
390
+ - 新增 src/utils/cloud.ts → 同目录 cloud.test.ts(formatCloudProvider 4 case)
391
+ - 新增 src/services/instance.ts:releaseInstance → instance.test.ts(成功 / 422 / tenant header 校验)
392
+ - 新增 src/hooks/useReleaseInstance.ts → useReleaseInstance.test.tsx(success / error / 未确认 dangerName 不调 mutate)
393
+ - 复用 src/components/CloudBadge.tsx → 已有 test 覆盖 aws/aliyun/azure,无需补
394
+ - 切租户集成测试: ✅ TenantContext.test.tsx(invalidate + 新 tenantId header)
395
+ - 跳过测: src/pages/instances/new/index.tsx(纯组合,关键逻辑已在 hook / form 层测)
396
+ ```
397
+
398
+ 如果跳过测试,**必须**写明原因(纯组合 / ui 包已测 / biz-ui 已测 / 一次性脚本)。
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: teamix-evo-design-opentrek
3
+ description: |
4
+ Apply OpenTrek design system rules (philosophy, patterns, page-types, brand tone/voice, visual foundations) when AI generates or reviews a full UI screen / page in an OpenTrek-variant project.
5
+ TRIGGER when: user asks to "新建 / 优化 / 重构 一个页面"、"做一个列表页 / 详情页 / 表单页 / 仪表盘"、"create / review / refactor a page、screen、dashboard、template"; intent involves layout structure, page-level information density, or multi-component composition; file write under `src/pages/**` or `src/templates/**`.
6
+ SKIP: single-component edits like "加个按钮"、"改 input 的 label"; pure tokens/theme overrides; pure code refactor with no visual change; teamix-evo lifecycle commands (defer to teamix-evo-manage).
7
+ Coordinates with: teamix-evo-code-opentrek (run alongside when the screen also creates new files).
8
+ ---
9
+
10
+ # teamix-evo-design-opentrek
11
+
12
+ > OpenTrek 设计体系 — 面向云控制台与企业级 B2B 产品的现代设计语言。
13
+ > 沉稳、克制、可预期,强调高密度信息下的清晰与效率。
14
+ >
15
+ > 本技能是 **完整自包含** 的 OpenTrek 设计规则(非 overlay)。
16
+ > Token 具体值请参照 `tokens/theme.css` 中的 CSS 变量,组件详细 API 通过 MCP `get_component_meta` 按需查询。
17
+
18
+ <!-- teamix-evo:managed:start id="core" -->
19
+
20
+ ## ⚠️ 执行协议(MANDATORY — 跳步即失败)
21
+
22
+ 收到页面相关请求时,**必须先识别意图,再进入对应的唯一执行路径**。不允许跳过路径中的任何步骤。
23
+
24
+ ### 意图路由
25
+
26
+ | 意图 | 识别关键词 | 唯一执行路径 |
27
+ | -------- | -------------------------------------------------- | ----------------------------------------------------------------------- |
28
+ | **生成** | "生成" / "创建" / "新建" / "设计一个" / "做一个" | **必须完整执行** [generation-flow.md](./generation-flow.md) §1 七步门控 |
29
+ | **翻新** | "改造" / "升级" / "翻新" / "对齐规范" / "重新生成" | **必须完整执行** [generation-flow.md](./generation-flow.md) §2 翻新路径 |
30
+ | **验证** | "检查" / "验证" / "评估" / "是否符合规范" | **必须完整执行** [generation-flow.md](./generation-flow.md) §3 验证路径 |
31
+ | **查询** | "token" / "色值" / "间距" / "组件尺寸" / "圆角" | → [foundations.md](./foundations.md)(MCP `tokens_*` 工具规划中) |
32
+
33
+ ### 不可跳过的前置读取
34
+
35
+ 无论哪种意图,进入 generation-flow.md 后**每个 Step 都有明确的 MUST READ 文件清单**。AI 不得凭"已有知识"跳过任何文件读取 — 每次任务必须重新读取,因为文件内容可能已更新。
36
+
37
+ ### 禁止行为
38
+
39
+ - ❌ 跳过 generation-flow.md 直接写代码
40
+ - ❌ 只读 patterns/\*.md 不读 philosophy.md / boundaries.md / foundations.md
41
+ - ❌ 选择性读取("这个文件我已经知道内容了")
42
+ - ❌ 把 SKILL.md 文件索引当作"我可以只挑一个读"的菜单
43
+
44
+ ## 文件索引
45
+
46
+ | 文件 | 职责 |
47
+ | -------------------------------------------------------- | --------------------------------------------------------------------- |
48
+ | [philosophy.md](./philosophy.md) | 设计哲学 + 四大原则 + 方法论 |
49
+ | [generation-flow.md](./generation-flow.md) | AI 六步生成/翻新/验证流程 |
50
+ | [boundaries.md](./boundaries.md) | 硬约束:F/FF/S/C/I 五组共 38 条规则([ERROR]/[WARN]) |
51
+ | [checklist.md](./checklist.md) | 10 项自检清单 |
52
+ | [foundations.md](./foundations.md) | Token 用法 + 排版 + 间距 + 色彩 + 圆角 + 阴影 + 动效 |
53
+ | [components.md](./components.md) | 组件选型决策树 + 双层架构索引 + 组合规则 |
54
+ | [patterns/page-types.md](./patterns/page-types.md) | 5 种页面类型 + 识别决策树 + Zone Map + 页间流转(§6) |
55
+ | [patterns/list-page.md](./patterns/list-page.md) | 列表页模式 (6 种子类型) |
56
+ | [patterns/detail-page.md](./patterns/detail-page.md) | 详情页模式 |
57
+ | [patterns/form-page.md](./patterns/form-page.md) | 表单页模式 (含 wizard) |
58
+ | [patterns/dashboard.md](./patterns/dashboard.md) | Dashboard 数据概览(StatCard / Chart 标准结构) |
59
+ | [patterns/sidebar.md](./patterns/sidebar.md) | 侧边栏统一规范(3 级 / 2 级结构、收起态、品牌色槽位、禁止清单) |
60
+ | [patterns/color-mapping.md](./patterns/color-mapping.md) | v4.1 语义色映射表(主色 / 状态 5 档 / gray 8 / accent 10 / chart 10) |
61
+ | [flows.md](./flows.md) | 4 条核心用户旅程 + 流转自检 |
62
+ | [brand.md](./brand.md) | OpenTrek 品牌调性 + 文案语气 + 正反例 |
63
+
64
+ <!-- teamix-evo:managed:end -->