@edupia-tutor/spec-driven-docs 0.14.0 → 0.14.1
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/commands/debug.md +435 -435
- package/commands/debug.tmpl +111 -111
- package/commands/define-product.md +330 -327
- package/commands/define-product.tmpl +50 -47
- package/commands/dev-gen-test.md +364 -364
- package/commands/dev-gen-test.tmpl +63 -63
- package/commands/dev-run-test.md +375 -375
- package/commands/dev-run-test.tmpl +74 -74
- package/commands/dev-smoke-test.md +340 -340
- package/commands/dev-smoke-test.tmpl +60 -60
- package/commands/fix-bug.md +402 -402
- package/commands/fix-bug.tmpl +78 -78
- package/commands/generate-bdd.md +512 -512
- package/commands/generate-bdd.tmpl +211 -211
- package/commands/generate-code.md +480 -482
- package/commands/generate-code.tmpl +179 -181
- package/commands/generate-design-spec.md +495 -495
- package/commands/generate-design-spec.tmpl +219 -219
- package/commands/generate-prd.md +445 -396
- package/commands/generate-prd.tmpl +45 -198
- package/commands/generate-spec-manifest.md +337 -337
- package/commands/generate-spec-manifest.tmpl +57 -57
- package/commands/generate-tech-docs.md +364 -364
- package/commands/generate-tech-docs.tmpl +84 -84
- package/commands/learn.md +346 -346
- package/commands/learn.tmpl +22 -22
- package/commands/map-testids.md +321 -321
- package/commands/map-testids.tmpl +41 -41
- package/commands/propose-scenario.md +334 -334
- package/commands/propose-scenario.tmpl +54 -54
- package/commands/qc-analyze.md +322 -323
- package/commands/qc-analyze.tmpl +42 -43
- package/commands/qc-design-test.md +303 -303
- package/commands/qc-design-test.tmpl +23 -23
- package/commands/qc-plan.md +296 -296
- package/commands/qc-plan.tmpl +16 -16
- package/commands/qc-report.md +301 -301
- package/commands/qc-report.tmpl +21 -21
- package/commands/qc-review.md +297 -297
- package/commands/qc-review.tmpl +17 -17
- package/commands/qc-run-test.md +336 -336
- package/commands/qc-run-test.tmpl +35 -35
- package/commands/refine-prd.md +426 -428
- package/commands/refine-prd.tmpl +61 -61
- package/commands/report-bug.md +350 -350
- package/commands/report-bug.tmpl +70 -70
- package/commands/review-code.md +363 -363
- package/commands/review-code.tmpl +39 -39
- package/commands/review-context.md +577 -579
- package/commands/review-context.tmpl +212 -212
- package/commands/review-tech-docs.md +426 -426
- package/commands/review-tech-docs.tmpl +146 -146
- package/commands/setup-ai-first.md +237 -237
- package/commands/setup-ai-first.tmpl +131 -131
- package/commands/sync.md +145 -145
- package/commands/sync.tmpl +93 -93
- package/commands/update-framework.md +88 -88
- package/commands/update-framework.tmpl +36 -36
- package/commands/validate-traces.md +379 -379
- package/commands/validate-traces.tmpl +99 -99
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/debug.md +435 -435
- package/core/commands/define-product.md +330 -327
- package/core/commands/dev-gen-test.md +364 -364
- package/core/commands/dev-run-test.md +375 -375
- package/core/commands/dev-smoke-test.md +340 -340
- package/core/commands/fix-bug.md +402 -402
- package/core/commands/generate-bdd.md +512 -512
- package/core/commands/generate-code.md +480 -482
- package/core/commands/generate-design-spec.md +495 -495
- package/core/commands/generate-prd.md +445 -396
- package/core/commands/generate-spec-manifest.md +337 -337
- package/core/commands/generate-tech-docs.md +364 -364
- package/core/commands/learn.md +346 -346
- package/core/commands/map-testids.md +321 -321
- package/core/commands/propose-scenario.md +334 -334
- package/core/commands/qc-analyze.md +322 -323
- package/core/commands/qc-design-test.md +303 -303
- package/core/commands/qc-plan.md +296 -296
- package/core/commands/qc-report.md +301 -301
- package/core/commands/qc-review.md +297 -297
- package/core/commands/qc-run-test.md +336 -336
- package/core/commands/refine-prd.md +426 -428
- package/core/commands/report-bug.md +350 -350
- package/core/commands/review-code.md +363 -363
- package/core/commands/review-context.md +577 -579
- package/core/commands/review-tech-docs.md +426 -426
- package/core/commands/setup-ai-first.md +237 -237
- package/core/commands/sync.md +145 -145
- package/core/commands/update-framework.md +88 -88
- package/core/commands/validate-traces.md +379 -379
- package/core/skills/code/SKILL.md +388 -388
- package/core/skills/debug/SKILL.md +390 -390
- package/core/skills/design-spec/SKILL.md +316 -316
- package/core/skills/discovery/SKILL.md +7 -547
- package/core/skills/prd/SKILL.md +298 -394
- package/core/skills/setup-ai-first/SKILL.md +79 -79
- package/core/skills/spec/SKILL.md +176 -176
- package/core/skills/test/SKILL.md +602 -602
- package/core/steps/capture-lesson.md +44 -44
- package/core/steps/context-loader.md +174 -174
- package/core/steps/gate.md +54 -54
- package/core/steps/report-footer.md +52 -52
- package/core/steps/review-fanout.md +85 -87
- package/core/steps/spawn-agent.md +45 -45
- package/core/steps/trace-mirror.md +21 -21
- package/core/templates/architecture.template.md +37 -37
- package/core/templates/design-spec.template.md +77 -77
- package/core/templates/platform-guide.template.md +47 -47
- package/core/templates/prd.template.md +106 -231
- package/core/templates/product-definition.template.md +101 -88
- package/docs/04-operations/publishing.md +20 -3
- package/package.json +1 -1
- package/skills/code/SKILL.md +388 -388
- package/skills/code/SKILL.tmpl +56 -56
- package/skills/debug/SKILL.md +390 -390
- package/skills/debug/SKILL.tmpl +60 -60
- package/skills/design-spec/SKILL.md +316 -316
- package/skills/design-spec/SKILL.tmpl +36 -36
- package/skills/discovery/SKILL.md +7 -547
- package/skills/discovery/SKILL.tmpl +7 -140
- package/skills/prd/SKILL.md +298 -394
- package/skills/prd/SKILL.tmpl +40 -151
- package/skills/setup-ai-first/SKILL.md +79 -79
- package/skills/setup-ai-first/SKILL.tmpl +27 -27
- package/skills/spec/SKILL.md +176 -176
- package/skills/spec/SKILL.tmpl +18 -18
- package/skills/test/SKILL.md +602 -602
- package/skills/test/SKILL.tmpl +44 -44
- package/steps/capture-lesson.md +44 -44
- package/steps/context-loader.md +174 -174
- package/steps/gate.md +54 -54
- package/steps/report-footer.md +52 -52
- package/steps/review-fanout.md +85 -87
- package/steps/spawn-agent.md +45 -45
- package/steps/trace-mirror.md +21 -21
- package/templates/architecture.template.md +37 -37
- package/templates/design-spec.template.md +77 -77
- package/templates/platform-guide.template.md +47 -47
- package/templates/prd.template.md +106 -231
- package/templates/product-definition.template.md +101 -88
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# /dev-run-test —
|
|
1
|
+
# /dev-run-test — Chạy Dev Self-Check Tests & Report kết quả
|
|
2
2
|
|
|
3
|
-
> **Scope — dev self-check (smoke),
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
> dev
|
|
3
|
+
> **Scope — dev self-check (smoke), không phải bộ test chính thức.** Chạy các test do
|
|
4
|
+
> `/dev-gen-test` sinh ra để dev xác nhận code mình chạy được trước khi review. Đây là một
|
|
5
|
+
> self-check của dev, **không** phải lần chạy test authoritative của QC/dev-team (flow riêng).
|
|
6
|
+
> Pass/fail được publish lên Living Docs như tín hiệu **dev self-test** — nó cho QC biết
|
|
7
|
+
> dev đã chạy check của họ; KHÔNG phải tuyên bố về độ phủ test chính thức.
|
|
8
8
|
|
|
9
9
|
## Gate
|
|
10
10
|
{{include:steps/gate.md}}
|
|
11
11
|
|
|
12
|
-
*
|
|
12
|
+
*Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID hoặc tên service. Context loading cung cấp `conventions.test_command` và `tech_stack.module`.*
|
|
13
13
|
|
|
14
14
|
## Context
|
|
15
15
|
{{include:steps/context-loader.md}}
|
|
@@ -18,20 +18,20 @@
|
|
|
18
18
|
|
|
19
19
|
## Service Detection
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
Đọc `active_module` từ context (đã phân giải ở context-loader Bước 1).
|
|
22
|
+
Dùng nó để chọn đúng lệnh chạy và bảng phân tích lỗi bên dưới.
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
26
|
## Submodule Working Directory
|
|
27
27
|
|
|
28
|
-
*
|
|
28
|
+
*Bỏ qua section này nếu `service_root` chưa được set (single-service mode).*
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Khi chạy ở **umbrella/submodule mode** (`service_root` được phân giải ở context-loader Bước 1.6):
|
|
31
31
|
|
|
32
|
-
-
|
|
33
|
-
- `conventions.test_command`
|
|
34
|
-
- Prefix
|
|
32
|
+
- Mọi lệnh trong section **Run** bên dưới phải thực thi từ trong `{service_root}/`
|
|
33
|
+
- `conventions.test_command` được nạp từ `{service_root}/.agent/project-context.yaml` — đã riêng theo service
|
|
34
|
+
- Prefix mọi lệnh shell bằng `cd {service_root} &&`:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
cd {service_root}
|
|
@@ -44,13 +44,13 @@ npx vitest run src/... # web-frontend
|
|
|
44
44
|
flutter test test/{domain}/... # flutter
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
> **
|
|
47
|
+
> **Vì sao cd?** Session Claude Code mở ở umbrella root. Mỗi service submodule có build tool, test runner, và cây dependency riêng — test phải chạy từ trong thư mục service.
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
51
|
## Run
|
|
52
52
|
|
|
53
|
-
###
|
|
53
|
+
### Nếu `platform_type = backend`
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
# Run all tests for this UC
|
|
@@ -71,7 +71,7 @@ pytest tests/{domain}/{test_file}.py::{TestClass}::{test_method} -v
|
|
|
71
71
|
pytest tests/ --cov={source_dir} --cov-report=term-missing
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
###
|
|
74
|
+
### Nếu `platform_type = web-frontend`
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
# Run all tests
|
|
@@ -87,7 +87,7 @@ npx playwright test {UC-ID}
|
|
|
87
87
|
npx cypress run --spec "cypress/e2e/{UC-ID}*"
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
###
|
|
90
|
+
### Nếu `platform_type = mobile`
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
93
|
# Flutter:
|
|
@@ -105,7 +105,7 @@ xcodebuild test -scheme {Scheme} -destination 'platform=iOS Simulator,name=iPhon
|
|
|
105
105
|
./gradlew connectedAndroidTest # instrumented (device/emulator required)
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
> **
|
|
108
|
+
> **Lưu ý cho Android instrumented test:** cần một emulator đang chạy hoặc device kết nối trước khi chạy `connectedAndroidTest`. Khởi động qua Android Studio hoặc: `emulator -avd {AVD_NAME} &`
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
@@ -115,86 +115,86 @@ xcodebuild test -scheme {Scheme} -destination 'platform=iOS Simulator,name=iPhon
|
|
|
115
115
|
|
|
116
116
|
#### java-spring / golang / dotnet / php-laravel
|
|
117
117
|
|
|
118
|
-
| Error Pattern |
|
|
118
|
+
| Error Pattern | Nguyên nhân thường gặp | Suggested Fix |
|
|
119
119
|
|---|---|---|
|
|
120
|
-
| `NullPointerException` |
|
|
121
|
-
| `Bean not found` |
|
|
122
|
-
| `Expected 200, got 401` |
|
|
123
|
-
| `Expected 200, got 400` | Request body
|
|
124
|
-
| `Expected 200, got 403` |
|
|
125
|
-
| `LazyInitializationException` | Lazy collection
|
|
126
|
-
| `Mapper not found` | Code
|
|
127
|
-
| `DataIntegrityViolationException` |
|
|
128
|
-
| Assertion mismatch |
|
|
120
|
+
| `NullPointerException` | Thiếu setup mock | Kiểm tra `given(...)`/`coEvery`/`mockk` cho dependency null |
|
|
121
|
+
| `Bean not found` | Thiếu khai báo mock | Thêm `@MockBean` / inject mock |
|
|
122
|
+
| `Expected 200, got 401` | Thiếu setup auth | Thêm auth token/user vào test context |
|
|
123
|
+
| `Expected 200, got 400` | Request body fail validation | Kiểm tra field bắt buộc trong DTO |
|
|
124
|
+
| `Expected 200, got 403` | Sai role | Thêm đúng role cho test user |
|
|
125
|
+
| `LazyInitializationException` | Lazy collection ngoài transaction | Thêm `@Transactional` hoặc eager fetch |
|
|
126
|
+
| `Mapper not found` | Code chưa compile | Chạy build trước khi test |
|
|
127
|
+
| `DataIntegrityViolationException` | Trùng key trong DB setup | Dùng `@Transactional` + rollback, hoặc clean DB giữa các test |
|
|
128
|
+
| Assertion mismatch | Sai giá trị mock return | Đọc lại setup `given(...).willReturn(...)` |
|
|
129
129
|
|
|
130
130
|
#### context-engineering (AI/LLM pipelines)
|
|
131
131
|
|
|
132
|
-
| Error Pattern |
|
|
132
|
+
| Error Pattern | Nguyên nhân thường gặp | Suggested Fix |
|
|
133
133
|
|---|---|---|
|
|
134
|
-
| `AssertionError`
|
|
135
|
-
| `ValidationError`
|
|
136
|
-
| `ConnectionError` / `APIError` |
|
|
137
|
-
| `TimeoutError` | Test
|
|
138
|
-
|
|
|
134
|
+
| `AssertionError` trên output mock LLM | Giá trị mock return không khớp schema | Kiểm tra lại setup `mock_llm.return_value` / `mock_llm.complete.return_value` |
|
|
135
|
+
| `ValidationError` trên response | Cấu trúc output LLM không khớp schema kỳ vọng | Siết schema check hoặc thêm retry logic trong test |
|
|
136
|
+
| `ConnectionError` / `APIError` | LLM API thật bị gọi trong test | Đảm bảo mock `patch('...')` được áp dụng — không bao giờ gọi LLM thật trong unit test |
|
|
137
|
+
| `TimeoutError` | Test gọi LLM endpoint live | Thêm mock; kiểm tra test fixture |
|
|
138
|
+
| Kết quả flaky / non-deterministic | Response LLM thật dùng trong assertion | Thay bằng giá trị mock return tất định |
|
|
139
139
|
|
|
140
140
|
### Web frontend failure patterns
|
|
141
141
|
|
|
142
|
-
| Error Pattern |
|
|
142
|
+
| Error Pattern | Nguyên nhân thường gặp | Suggested Fix |
|
|
143
143
|
|---|---|---|
|
|
144
|
-
| `Unable to find role "..."` | Element
|
|
145
|
-
| `TestingLibraryElementError: Found multiple elements` | Selector
|
|
146
|
-
| `Network request not intercepted` |
|
|
147
|
-
| `act(...)` warning | State update
|
|
148
|
-
| `Cannot read properties of undefined` | Component
|
|
149
|
-
| Playwright timeout | Page
|
|
150
|
-
| `expect(page.locator(...)).toBeVisible`
|
|
144
|
+
| `Unable to find role "..."` | Element chưa render | Bọc trong `await waitFor(() => ...)` |
|
|
145
|
+
| `TestingLibraryElementError: Found multiple elements` | Selector quá rộng | Dùng `getByRole(..., { name: '...' })` để thu hẹp |
|
|
146
|
+
| `Network request not intercepted` | Thiếu MSW handler / `cy.intercept` | Thêm handler cho endpoint |
|
|
147
|
+
| `act(...)` warning | State update sau khi test kết thúc | Await async event / `await userEvent.click(...)` |
|
|
148
|
+
| `Cannot read properties of undefined` | Component render trước khi data load | Thêm loading state hoặc mock data đã resolve |
|
|
149
|
+
| Playwright timeout | Page chưa navigate / element ẩn | Kiểm tra route, thêm `waitForSelector` |
|
|
150
|
+
| `expect(page.locator(...)).toBeVisible` fail | Sai selector | Dùng Playwright Inspector để tìm đúng locator |
|
|
151
151
|
|
|
152
152
|
### Mobile failure patterns
|
|
153
153
|
|
|
154
154
|
#### Flutter
|
|
155
|
-
| Error Pattern |
|
|
155
|
+
| Error Pattern | Nguyên nhân thường gặp | Suggested Fix |
|
|
156
156
|
|---|---|---|
|
|
157
|
-
| `pumpAndSettle timed out` | Async operation
|
|
158
|
-
| `No widget found` | Widget
|
|
159
|
-
| `setState called after dispose` | Widget
|
|
160
|
-
| BLoC state mismatch |
|
|
157
|
+
| `pumpAndSettle timed out` | Async operation chưa hoàn thành | Dùng `pump(Duration(...))` cho delay cụ thể |
|
|
158
|
+
| `No widget found` | Widget chưa render / sai finder | Kiểm tra `find.byType`, `find.text`, `find.byKey` |
|
|
159
|
+
| `setState called after dispose` | Widget bị dispose trước khi async xong | Cancel async trong `dispose()` |
|
|
160
|
+
| BLoC state mismatch | Sai event emit | Verify `mockBloc` nhận đúng event |
|
|
161
161
|
|
|
162
162
|
#### React Native
|
|
163
|
-
| Error Pattern |
|
|
163
|
+
| Error Pattern | Nguyên nhân thường gặp | Suggested Fix |
|
|
164
164
|
|---|---|---|
|
|
165
|
-
| `Unable to find element` |
|
|
166
|
-
| `act(...)` warning | Async state
|
|
167
|
-
|
|
|
165
|
+
| `Unable to find element` | Thiếu `testID` hoặc sai query | Thêm `accessibilityLabel` hoặc `testID` vào component |
|
|
166
|
+
| `act(...)` warning | Async state update | Bọc trong `act(async () => { ... })` |
|
|
167
|
+
| Thiếu navigation mock | `useNavigation` chưa mock | Thêm jest mock cho `@react-navigation/native` |
|
|
168
168
|
|
|
169
169
|
#### iOS / Android
|
|
170
|
-
| Error Pattern |
|
|
170
|
+
| Error Pattern | Nguyên nhân thường gặp | Suggested Fix |
|
|
171
171
|
|---|---|---|
|
|
172
|
-
| `XCTAssertEqual failed` |
|
|
173
|
-
| `Compose node not found` |
|
|
174
|
-
| `Hilt injection failed` |
|
|
175
|
-
| Emulator not available | `connectedAndroidTest`
|
|
172
|
+
| `XCTAssertEqual failed` | Sai giá trị kỳ vọng | Kiểm tra output ViewModel cho mock đã cho |
|
|
173
|
+
| `Compose node not found` | Sai `contentDescription` / `testTag` | Thêm `Modifier.testTag(...)` vào composable |
|
|
174
|
+
| `Hilt injection failed` | Thiếu test module | Thêm `@UninstallModules` + `@BindValue` trong test class |
|
|
175
|
+
| Emulator not available | `connectedAndroidTest` không có device | Khởi động emulator trước, chờ nó boot |
|
|
176
176
|
|
|
177
177
|
---
|
|
178
178
|
|
|
179
179
|
## Write Trace State
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
Sau khi chạy, lưu kết quả vào **TSV authoritative** trong service để chúng tới được
|
|
182
|
+
report Living Docs ở spec module (qua `/sync` + `/validate-traces`). Các file test
|
|
183
|
+
ở lại trong service — chỉ *status* của lần chạy được report.
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
Cập nhật `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}.tsv` (nếu `domain`/`prd_slug` không phân giải được từ spec target, định vị TSV bằng cách glob `{paths.trace_dir}/**/{UC-ID}.tsv` — nó được tạo trước đó bởi `/generate-bdd`) — cho mỗi scenario row (khớp `sc_id` qua tag
|
|
186
|
+
`@trace.verifies={UC-ID}-SC{N}` của test). *(Umbrella + `spec_source`: `trace_dir` là `{spec_source}/.trace` — test chạy từ `service_root` nhưng update `dev_selftest` ghi vào **spec repo**; commit/push spec submodule cho nó.)*
|
|
187
187
|
|
|
188
|
-
|
|
|
188
|
+
| Cột | Giá trị |
|
|
189
189
|
|--------|-------|
|
|
190
|
-
| `dev_selftest` | `pass`
|
|
191
|
-
| `dev_selftest_at` |
|
|
190
|
+
| `dev_selftest` | `pass` nếu mọi test của SC này pass · `fail` nếu có cái fail · `not_run` nếu test của nó bị skip/vắng |
|
|
191
|
+
| `dev_selftest_at` | hôm nay `YYYY-MM-DD` |
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
Giữ nguyên mọi cột khác — đặc biệt **không bao giờ** đụng `qc_status`/`qc_run_at`
|
|
194
|
+
(kết quả QC automation chính thức, do `/qc-run-test` sở hữu). `dev_selftest` (dev smoke)
|
|
195
|
+
và `qc_status` (QC chính thức) là hai tín hiệu riêng. `dev_selftest`/`dev_selftest_at` cũng
|
|
196
|
+
trực giao với `status` (OK/GAP/DRIFT/UNTRACKED): `status` theo dõi *coverage*, `dev_selftest`
|
|
197
|
+
theo dõi *kết quả chạy* gần nhất của dev.
|
|
198
198
|
|
|
199
199
|
## Refresh Panel Mirror
|
|
200
200
|
{{include:steps/trace-mirror.md}}
|
|
@@ -212,13 +212,13 @@ signals. `dev_selftest`/`dev_selftest_at` are also orthogonal to `status`
|
|
|
212
212
|
|------|-------|------------|
|
|
213
213
|
|
|
214
214
|
## Recommendations
|
|
215
|
-
{
|
|
215
|
+
{fix cụ thể cho từng failure}
|
|
216
216
|
|
|
217
217
|
Trace: {paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}.tsv updated (dev_selftest, dev_selftest_at)
|
|
218
218
|
|
|
219
219
|
Next:
|
|
220
|
-
|
|
221
|
-
|
|
220
|
+
Mọi test pass → /review-code {UC-ID}
|
|
221
|
+
Test fail → /fix-bug {TICKET_ID} (bug thật) hoặc fix test (sai expectation)
|
|
222
222
|
|
|
223
|
-
📊 Living Docs:
|
|
223
|
+
📊 Living Docs: chạy /validate-traces (hoặc /sync) để push trace này lên dashboard spec-module.
|
|
224
224
|
```
|