sdtk-kit 0.3.8 → 0.3.9
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/assets/manifest/toolkit-bundle.manifest.json +39 -39
- package/assets/manifest/toolkit-bundle.sha256.txt +18 -18
- package/assets/toolkit/toolkit/install.ps1 +44 -4
- package/assets/toolkit/toolkit/scripts/install-claude-skills.ps1 +43 -3
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/SKILL.md +1 -1
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/scripts/generate_api_design_detail.py +87 -11
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/FLOW_ACTION_SPEC_CREATION_RULES.md +25 -5
- package/assets/toolkit/toolkit/skills/sdtk-design-layout/SKILL.md +16 -5
- package/assets/toolkit/toolkit/skills/sdtk-design-layout/scripts/render_design_layout_images.py +34 -1
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/SKILL.md +17 -4
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/FLOW_ACTION_SPEC_CREATION_RULES.md +25 -5
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/numbering-rules.md +20 -68
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/scripts/validate_flow_action_spec_numbering.py +168 -3
- package/assets/toolkit/toolkit/skills-claude/api-design-spec/SKILL.md +22 -8
- package/assets/toolkit/toolkit/skills-claude/arch/SKILL.md +26 -39
- package/assets/toolkit/toolkit/skills-claude/design-layout/SKILL.md +38 -6
- package/assets/toolkit/toolkit/skills-claude/screen-design-spec/SKILL.md +47 -25
- package/assets/toolkit/toolkit/templates/docs/design/DESIGN_LAYOUT_TEMPLATE.md +12 -1
- package/assets/toolkit/toolkit/templates/docs/specs/FLOW_ACTION_SPEC_CREATION_RULES.md +25 -5
- package/assets/toolkit/toolkit/templates/docs/specs/FLOW_ACTION_SPEC_TEMPLATE.md +34 -9
- package/package.json +1 -1
|
@@ -8,6 +8,8 @@ description: Generate UI screen layout documentation for a feature, including Pl
|
|
|
8
8
|
## Critical Constraints
|
|
9
9
|
- I do not skip screen IDs or item numbering alignment with the wireframe.
|
|
10
10
|
- I do not hide render limitations; I record when screen preview rendering is unavailable.
|
|
11
|
+
- I do not leave rendered wireframes without visible local item markers that map back to the `Items` table.
|
|
12
|
+
- I do not pretend wireframe markers are the same thing as `FLOW_ACTION_SPEC` global action-table numbers.
|
|
11
13
|
|
|
12
14
|
## Output
|
|
13
15
|
- `docs/design/DESIGN_LAYOUT_[FEATURE_KEY].md`
|
|
@@ -15,13 +17,23 @@ description: Generate UI screen layout documentation for a feature, including Pl
|
|
|
15
17
|
## Process
|
|
16
18
|
1. Read BA spec (screens + fields) and API list.
|
|
17
19
|
2. For each screen, include:
|
|
18
|
-
- PlantUML `@startsalt` wireframe first
|
|
20
|
+
- PlantUML `@startsalt` wireframe first, with visible screen-local markers embedded directly in the SALT labels
|
|
19
21
|
- API list table
|
|
20
|
-
- Item table
|
|
21
|
-
3.
|
|
22
|
-
|
|
22
|
+
- Item table whose `No` values exactly match the local marker sequence used in the wireframe for that screen
|
|
23
|
+
3. Use this wireframe-marker convention so the rendered SVG can be cross-referenced visually:
|
|
24
|
+
- markers are screen-local visual references and reset per screen
|
|
25
|
+
- prefer Unicode circled-number markers in generated docs; avoid parenthetical `(N)` markers because they blend into UI text and can conflict with SALT parsing
|
|
26
|
+
- text label or input prompt: prefix the visible label with the local marker
|
|
27
|
+
- button: place the local marker inside the visible button label
|
|
28
|
+
- table header: prefix the header text with the local marker
|
|
29
|
+
- standalone control, pagination, toggle, or status chip: prefix the visible label with the local marker
|
|
30
|
+
- do not rely on prose, comments, or a separate legend; the marker must be visible inside the rendered wireframe itself
|
|
31
|
+
- the local marker does not need to equal the `FLOW_ACTION_SPEC` global `No`; `screen-design-spec` publishes the mapping table
|
|
32
|
+
4. Keep screen IDs consistent (A-*, B-*, C-*).
|
|
33
|
+
5. After writing `DESIGN_LAYOUT`, attempt to render screen preview images by default:
|
|
23
34
|
- Run `render_design_layout_images.py` to extract `@startsalt` blocks and render `.svg` files.
|
|
24
35
|
- Output path: `docs/specs/assets/<feature_snake>/screens/<screen_id>.svg`
|
|
36
|
+
- The renderer warns if a screen wireframe has no visible local markers or still uses legacy `(N)` markers.
|
|
25
37
|
- If PlantUML is unavailable, rendering is skipped with a warning and the render-skipped note is used in `FLOW_ACTION_SPEC`. The layout doc remains valid.
|
|
26
38
|
|
|
27
39
|
## Screen Image Renderer
|
|
@@ -38,4 +50,3 @@ description: Generate UI screen layout documentation for a feature, including Pl
|
|
|
38
50
|
|
|
39
51
|
## Template
|
|
40
52
|
- Use `toolkit/templates/docs/design/DESIGN_LAYOUT_TEMPLATE.md` as starting structure.
|
|
41
|
-
|
package/assets/toolkit/toolkit/skills/sdtk-design-layout/scripts/render_design_layout_images.py
CHANGED
|
@@ -21,6 +21,10 @@ from pathlib import Path
|
|
|
21
21
|
from typing import List, Optional, Tuple
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
CIRCLED_ITEM_MARKER_RE = re.compile(r"[\u2460-\u2473\u3251-\u325F\u32B1-\u32BF]")
|
|
25
|
+
LEGACY_ITEM_MARKER_RE = re.compile(r"\(\d+\)")
|
|
26
|
+
|
|
27
|
+
|
|
24
28
|
def parse_args() -> argparse.Namespace:
|
|
25
29
|
parser = argparse.ArgumentParser(
|
|
26
30
|
description="Render screen images from DESIGN_LAYOUT PlantUML SALT blocks."
|
|
@@ -100,12 +104,20 @@ def extract_screens(text: str) -> List[Tuple[str, str]]:
|
|
|
100
104
|
return screens
|
|
101
105
|
|
|
102
106
|
|
|
107
|
+
def count_visible_item_markers(salt_block: str) -> int:
|
|
108
|
+
return len(CIRCLED_ITEM_MARKER_RE.findall(salt_block))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def count_legacy_item_markers(salt_block: str) -> int:
|
|
112
|
+
return len(LEGACY_ITEM_MARKER_RE.findall(salt_block))
|
|
113
|
+
|
|
114
|
+
|
|
103
115
|
def render_svg(
|
|
104
116
|
plantuml_jar: Path, puml_path: Path, output_dir: Path
|
|
105
117
|
) -> Optional[str]:
|
|
106
118
|
"""Render a single .puml to .svg. Returns error string or None on success."""
|
|
107
119
|
proc = subprocess.run(
|
|
108
|
-
["java", "-jar", str(plantuml_jar), "-tsvg", str(puml_path)],
|
|
120
|
+
["java", "-Dfile.encoding=UTF-8", "-jar", str(plantuml_jar), "-charset", "UTF-8", "-tsvg", str(puml_path)],
|
|
109
121
|
stdout=subprocess.PIPE,
|
|
110
122
|
stderr=subprocess.STDOUT,
|
|
111
123
|
text=True,
|
|
@@ -166,6 +178,19 @@ def main() -> int:
|
|
|
166
178
|
|
|
167
179
|
print(f"[OK] Found {len(screens)} screen(s) with SALT wireframes.")
|
|
168
180
|
|
|
181
|
+
numbering_warnings: List[str] = []
|
|
182
|
+
for screen_id, salt_block in screens:
|
|
183
|
+
marker_count = count_visible_item_markers(salt_block)
|
|
184
|
+
legacy_marker_count = count_legacy_item_markers(salt_block)
|
|
185
|
+
if legacy_marker_count > 0:
|
|
186
|
+
numbering_warnings.append(
|
|
187
|
+
f"{screen_id}: legacy `(N)` markers detected; prefer circled-number markers to avoid SALT ambiguity."
|
|
188
|
+
)
|
|
189
|
+
elif marker_count == 0:
|
|
190
|
+
numbering_warnings.append(
|
|
191
|
+
f"{screen_id}: no visible wireframe item markers found."
|
|
192
|
+
)
|
|
193
|
+
|
|
169
194
|
# Write .puml files
|
|
170
195
|
puml_files: List[Path] = []
|
|
171
196
|
for screen_id, salt_block in screens:
|
|
@@ -176,6 +201,10 @@ def main() -> int:
|
|
|
176
201
|
|
|
177
202
|
if args.skip_render:
|
|
178
203
|
print(f"[OK] Wrote {len(puml_files)} .puml file(s). Render skipped (--skip-render).")
|
|
204
|
+
if numbering_warnings:
|
|
205
|
+
print("[WARN] Wireframe numbering issues:")
|
|
206
|
+
for item in numbering_warnings:
|
|
207
|
+
print(f" - {item}")
|
|
179
208
|
return 0
|
|
180
209
|
|
|
181
210
|
# Find PlantUML
|
|
@@ -201,6 +230,10 @@ def main() -> int:
|
|
|
201
230
|
print("[WARN] Render issues:")
|
|
202
231
|
for item in errors:
|
|
203
232
|
print(f" - {item}")
|
|
233
|
+
if numbering_warnings:
|
|
234
|
+
print("[WARN] Wireframe numbering issues:")
|
|
235
|
+
for item in numbering_warnings:
|
|
236
|
+
print(f" - {item}")
|
|
204
237
|
|
|
205
238
|
return 0
|
|
206
239
|
|
|
@@ -8,6 +8,10 @@ description: Create/update screen flow-action specifications from requirement so
|
|
|
8
8
|
## Critical Constraints
|
|
9
9
|
- I do not finalize UI-scope screens without a valid design source type and reference.
|
|
10
10
|
- I do not leave broken image paths or incomplete API mappings in the flow-action spec.
|
|
11
|
+
- I use new-style PlantUML activity diagram syntax only for the screen flow diagram.
|
|
12
|
+
- I do not mix legacy and new-style PlantUML activity syntax in the screen flow diagram.
|
|
13
|
+
- Do not mix legacy activity syntax such as `(*)`, `-->`, or `[edge label]` with new-style activity actions like `:Activity;`.
|
|
14
|
+
- I keep action-table numbering global across the document even when wireframe markers reset per screen.
|
|
11
15
|
|
|
12
16
|
## Outputs
|
|
13
17
|
- `docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md`
|
|
@@ -35,6 +39,7 @@ description: Create/update screen flow-action specifications from requirement so
|
|
|
35
39
|
5. For each screen/dialog:
|
|
36
40
|
- Add metadata (screen ID, Design Source Type, Design Source Reference)
|
|
37
41
|
- Add screen image reference (from Figma, screenshot, or rendered `.svg` from generated layout)
|
|
42
|
+
- For generated-draft wireframes, add a wireframe-marker mapping table from the screen-local wireframe markers to the global action-table `No`
|
|
38
43
|
- Add UI item/action table with `No` column
|
|
39
44
|
- Add API mapping table (trigger -> API -> data usage)
|
|
40
45
|
6. Build/update `System processing flow` from use-case and process sources.
|
|
@@ -43,15 +48,21 @@ description: Create/update screen flow-action specifications from requirement so
|
|
|
43
48
|
9. Validate:
|
|
44
49
|
- global numbering consistency (no screen-level reset)
|
|
45
50
|
- no broken image paths (for `generated-draft` screens, verify `.svg` exists or use render-skipped note)
|
|
46
|
-
- PlantUML renderability
|
|
51
|
+
- PlantUML renderability and new-style activity syntax consistency (no `(*)`, `-->`, or `[edge label]` mixing)
|
|
47
52
|
- consistency with API endpoints spec
|
|
48
53
|
- EN artifact hygiene (no VI leftovers, no mojibake, no merged heading lines)
|
|
49
54
|
- for legacy specs with per-screen reset numbering: run renumber migration first
|
|
50
55
|
- every UI-scope screen declares a Design Source Type (`source-backed` or `generated-draft`)
|
|
56
|
+
- every generated-draft screen with an embedded wireframe image includes a wireframe-marker mapping table to the global action-table `No`
|
|
51
57
|
10. For `generated-draft` screens, set `Screen image:` to the rendered `.svg` path if the file exists:
|
|
52
|
-
- Expected path: `docs/specs/assets/<feature_snake>/screens/<screen_id>.svg`
|
|
58
|
+
- Expected path (filesystem): `docs/specs/assets/<feature_snake>/screens/<screen_id>.svg`
|
|
59
|
+
- Markdown image path (file-relative): `assets/<feature_snake>/screens/<screen_id>.svg`
|
|
53
60
|
- If the `.svg` does not exist (render unavailable), replace the image reference with: `> Screen image not rendered in this environment. See Design Source Reference for layout.`
|
|
54
|
-
11.
|
|
61
|
+
11. For generated-draft screens, treat wireframe markers as screen-local visual references:
|
|
62
|
+
- the local marker sequence may reset per screen
|
|
63
|
+
- do not renumber the wireframe to fake equality with the global action-table `No`
|
|
64
|
+
- publish the mapping table directly under the screen image so reviewers can cross-reference local markers to global action-table numbers
|
|
65
|
+
12. Update document history and handoff to ARCH/DEV.
|
|
55
66
|
|
|
56
67
|
## Rule References
|
|
57
68
|
- Core rules: `./references/FLOW_ACTION_SPEC_CREATION_RULES.md`
|
|
@@ -63,6 +74,8 @@ description: Create/update screen flow-action specifications from requirement so
|
|
|
63
74
|
- `scripts/validate_flow_action_spec_numbering.py`
|
|
64
75
|
- Checks duplicate/resetted numbering in action tables.
|
|
65
76
|
- Checks encoding/markdown hygiene.
|
|
77
|
+
- Checks screen-image markdown path convention.
|
|
78
|
+
- Checks mixed legacy/new-style PlantUML activity syntax in screen-flow diagrams.
|
|
66
79
|
- For EN artifacts, run with `--en-check`:
|
|
67
80
|
- `python "toolkit/skills/sdtk-screen-design-spec/scripts/validate_flow_action_spec_numbering.py" --spec "docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md" --en-check`
|
|
68
81
|
- `scripts/renumber_flow_action_spec_global.py`
|
|
@@ -70,4 +83,4 @@ description: Create/update screen flow-action specifications from requirement so
|
|
|
70
83
|
- Dry-run:
|
|
71
84
|
- `python "toolkit/skills/sdtk-screen-design-spec/scripts/renumber_flow_action_spec_global.py" --spec "docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md"`
|
|
72
85
|
- Apply:
|
|
73
|
-
- `python "toolkit/skills/sdtk-screen-design-spec/scripts/renumber_flow_action_spec_global.py" --spec "docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md" --write`
|
|
86
|
+
- `python "toolkit/skills/sdtk-screen-design-spec/scripts/renumber_flow_action_spec_global.py" --spec "docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md" --write`
|
|
@@ -94,6 +94,7 @@ If a section is not in scope, keep the section and mark explicitly as `N/A` with
|
|
|
94
94
|
|
|
95
95
|
- Use one numbering mode only: `global across document`.
|
|
96
96
|
- Number values must increase across all action tables in the document.
|
|
97
|
+
- DESIGN_LAYOUT wireframe markers are a separate screen-local visual system. They may reset per screen and do not need to numerically equal the global action-table `No`.
|
|
97
98
|
- Avoid duplicate item descriptions for the same UI control across screens unless behavior differs.
|
|
98
99
|
- If duplicate number is intentional (rare), annotate reason in `Note`.
|
|
99
100
|
|
|
@@ -127,6 +128,19 @@ Rules:
|
|
|
127
128
|
- The flow-action spec and the design-layout doc must remain separate documents.
|
|
128
129
|
- When Figma becomes available later, update the source mode from `generated-draft` to `source-backed`.
|
|
129
130
|
|
|
131
|
+
## 5.2 Wireframe Marker Mapping for Generated-Draft Screens
|
|
132
|
+
|
|
133
|
+
For generated-draft screens with rendered design-layout images:
|
|
134
|
+
|
|
135
|
+
- Treat wireframe markers as screen-local visual references only.
|
|
136
|
+
- Keep action-table `No` global across the document per section 4.
|
|
137
|
+
- Add a wireframe marker mapping table directly under the screen image.
|
|
138
|
+
- Use this stable header:
|
|
139
|
+
- `No | Wireframe Marker | Action Table No | Item Name | Notes`
|
|
140
|
+
- Map only the items visibly present on the wireframe image.
|
|
141
|
+
- If an action-table row is not visible on the wireframe, state `Not shown on wireframe` in `Notes` instead of inventing a marker.
|
|
142
|
+
- If the wireframe label and the action-table label differ slightly, keep the action-table item name and explain the label difference in `Notes`.
|
|
143
|
+
|
|
130
144
|
## 6. API Traceability Rules
|
|
131
145
|
|
|
132
146
|
- Every actionable UI event that changes state must map to a write API.
|
|
@@ -136,8 +150,11 @@ Rules:
|
|
|
136
150
|
|
|
137
151
|
## 7. PlantUML Rules for Screen Flow
|
|
138
152
|
|
|
139
|
-
- Use
|
|
140
|
-
-
|
|
153
|
+
- Use new-style PlantUML activity diagram syntax only for screen-flow diagrams.
|
|
154
|
+
- Allowed activity constructs include: `start`, `stop`, `partition "..." {}`, `:Activity;`, `->`, `if/then/else/endif`, `fork`, `fork again`, `end fork`, and `note right/left`.
|
|
155
|
+
- Do not mix legacy activity syntax such as `(*)`, `-->`, or `[edge label]` with new-style activity actions like `:Activity;`.
|
|
156
|
+
- Validate renderability before handoff. If a renderer is unavailable in the current environment, at minimum keep the block internally consistent with new-style activity syntax only.
|
|
157
|
+
- Use `\\n` for multi-line labels in activity nodes and notes.
|
|
141
158
|
- Keep diagram at navigation/action level (not low-level SQL or backend internals).
|
|
142
159
|
- Ensure screen names in PlantUML match section names in layout spec.
|
|
143
160
|
|
|
@@ -147,7 +164,8 @@ Rules:
|
|
|
147
164
|
1. Confirmed design source (for example Figma)
|
|
148
165
|
2. Confirmed requirement files (Excel/spec)
|
|
149
166
|
3. User-provided screenshots
|
|
150
|
-
- Store images in repository-relative paths.
|
|
167
|
+
- Store images in repository-relative filesystem paths.
|
|
168
|
+
- Reference images in markdown using file-relative paths from the spec file's directory.
|
|
151
169
|
- Do not keep temporary local absolute paths in markdown.
|
|
152
170
|
|
|
153
171
|
### 8.1 Rendered Screen Images for Generated-Draft Screens
|
|
@@ -155,8 +173,10 @@ Rules:
|
|
|
155
173
|
When `Design Source Type` is `generated-draft`:
|
|
156
174
|
- Screen preview images may be rendered from `DESIGN_LAYOUT_[FEATURE_KEY].md` PlantUML SALT wireframes.
|
|
157
175
|
- Renderer: `toolkit/skills/sdtk-design-layout/scripts/render_design_layout_images.py`
|
|
158
|
-
- Expected output path: `docs/specs/assets/<feature_snake>/screens/<screen_id>.svg`
|
|
159
|
-
-
|
|
176
|
+
- Expected output path (filesystem): `docs/specs/assets/<feature_snake>/screens/<screen_id>.svg`
|
|
177
|
+
- Markdown image path (file-relative from `docs/specs/*_FLOW_ACTION_SPEC.md`): `assets/<feature_snake>/screens/<screen_id>.svg`
|
|
178
|
+
- DESIGN_LAYOUT markers inside the image are screen-local visual references and may reset per screen; use the wireframe mapping table to bridge them to the global action-table `No`.
|
|
179
|
+
- If the rendered `.svg` file exists, use the file-relative markdown path in the `Screen image:` reference.
|
|
160
180
|
- If rendering is unavailable or the `.svg` does not exist, replace the image line with:
|
|
161
181
|
`> Screen image not rendered in this environment. See Design Source Reference for layout.`
|
|
162
182
|
- Do not leave a broken image reference pointing to a non-existent file.
|
|
@@ -1,76 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# UI Numbering Policy
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Scope
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
- **Không lặp mô tả** item đã xuất hiện ở màn hình trước.
|
|
5
|
+
- `FLOW_ACTION_SPEC` action tables use one numbering mode only: global across the full document.
|
|
6
|
+
- `DESIGN_LAYOUT` wireframe markers are screen-local visual references and may reset per screen.
|
|
7
|
+
- Do not force wireframe marker values to numerically equal the global action-table `No`.
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## DESIGN_LAYOUT Rules
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
11
|
+
- Every visible wireframe control should have a visible local marker.
|
|
12
|
+
- Prefer Unicode circled-number markers in generated design docs.
|
|
13
|
+
- Avoid parenthetical `(N)` markers because they blend into UI text and can conflict with SALT parser behavior.
|
|
14
|
+
- Local wireframe markers only cover items visibly present on that screen.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
- Màn hình sau **không thêm dòng mới** vào bảng action cho item này.
|
|
18
|
-
- Nếu cần người đọc định vị, vẫn có thể giữ số trên ảnh (để đối chiếu), nhưng ghi chú trong section: “Đã mô tả ở màn X (No Y)”.
|
|
16
|
+
## FLOW_ACTION_SPEC Rules
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
- Keep action-table `No` values global across the document.
|
|
19
|
+
- For each generated-draft screen with an embedded wireframe image, add a wireframe marker mapping table:
|
|
20
|
+
- `No | Wireframe Marker | Action Table No | Item Name | Notes`
|
|
21
|
+
- The mapping table bridges screen-local wireframe markers to global action-table numbers.
|
|
22
|
+
- If an action-table item is not visible on the wireframe, note that explicitly instead of inventing a marker.
|
|
21
23
|
|
|
22
|
-
1
|
|
23
|
-
2) Tạo “sổ tay đánh số” (mapping) dạng:
|
|
24
|
-
- `key` (tên/nhãn JP + loại control + khu vực)
|
|
25
|
-
- `value` (số No)
|
|
26
|
-
3) Với từng màn hình:
|
|
27
|
-
- Liệt kê item tương tác cần mô tả (button/link/selectbox/checkbox…).
|
|
28
|
-
- Nếu item mới: cấp số tiếp theo và thêm vào mapping.
|
|
29
|
-
- Nếu item cũ: không cấp số mới, không lặp mô tả.
|
|
30
|
-
4) Nếu **chưa xác định được vị trí đặt số trên ảnh**:
|
|
31
|
-
- Yêu cầu user cung cấp ảnh có đánh dấu hoặc chỉ rõ vị trí (tỷ lệ/tọa độ/ô Excel).
|
|
32
|
-
- Không tự suy đoán vị trí.
|
|
24
|
+
## Non-1:1 Mapping Cases
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
- Ví dụ:
|
|
38
|
-
- `BK205`: `㉕リスト表示切替ボタン`
|
|
39
|
-
- `BK245`: `㉖マグネット表示切替ボタン`
|
|
40
|
-
- Ảnh/screenshot đã được user đánh số (nguồn xác thực tốt nhất).
|
|
41
|
-
- Ảnh export từ Figma/Excel để overlay số.
|
|
42
|
-
|
|
43
|
-
## Case lỗi đã gặp & cách tránh
|
|
44
|
-
|
|
45
|
-
### 1) Trùng số “1” trong màn Search / Edit
|
|
46
|
-
|
|
47
|
-
Nguyên nhân thường gặp:
|
|
48
|
-
- Overlay tool chỉ đọc được “1” khi marker là 2 chữ số (10,11,12…).
|
|
49
|
-
|
|
50
|
-
Cách tránh:
|
|
51
|
-
- Khi tạo marker (oval) cho 2+ chữ số, tăng kích thước tối thiểu (>= 32px) và tắt WordWrap.
|
|
52
|
-
- Nếu lấy marker từ Excel, đảm bảo marker trong Excel hiển thị đúng 2 chữ số trước khi export.
|
|
53
|
-
|
|
54
|
-
### 2) Excel spec bị “duplicate numbering” (ví dụ ㉑ xuất hiện 2 lần)
|
|
55
|
-
|
|
56
|
-
Nguyên nhân:
|
|
57
|
-
- Dữ liệu Excel gốc bị trùng ký hiệu số.
|
|
58
|
-
|
|
59
|
-
Cách xử lý:
|
|
60
|
-
- Ưu tiên theo logic UI + screenshot user xác nhận.
|
|
61
|
-
- Renumber phần bị duplicate theo số tiếp theo (vd: 21 bị trùng → “đóng/xoá/search” thành 22/23/24 theo thứ tự thao tác).
|
|
62
|
-
- Ghi chú rõ “Excel có duplicate; đã chuẩn hoá lại theo sequence toàn cục”.
|
|
63
|
-
|
|
64
|
-
### 3) Item đã mô tả ở màn trước vẫn bị lặp trong bảng màn sau
|
|
65
|
-
|
|
66
|
-
Cách tránh:
|
|
67
|
-
- Trước khi thêm dòng vào bảng, đối chiếu mapping toàn cục.
|
|
68
|
-
- Nếu cần nhắc lại, chỉ thêm bullet note “đã mô tả ở …”, không thêm dòng mới trong bảng.
|
|
69
|
-
|
|
70
|
-
## Ví dụ theo dự án tham chiếu
|
|
71
|
-
|
|
72
|
-
- Màn 3.1 (Bukken View): đã đánh số 1–9.
|
|
73
|
-
- Màn 3.2 (Search): tiếp tục 10–24.
|
|
74
|
-
- Màn 3.3 (Bukken Magnet): chỉ đánh số 25 cho nút chuyển List.
|
|
75
|
-
- Màn 3.4 (Bukken List): chỉ đánh số 26 cho nút chuyển Magnet.
|
|
76
|
-
- Màn 3.7 (Worker Magnet): chỉ đánh số các item mới theo yêu cầu (vd: selectbox 物件名, nút 検索, checkbox 複数日登録モード).
|
|
26
|
+
- If the wireframe label and the action-table label differ slightly, use the action-table item name in the mapping table and explain the label difference in `Notes`.
|
|
27
|
+
- If multiple action-table rows map to one visual region, spell that out in `Notes`.
|
|
28
|
+
- If a dialog, hidden state, or validation item is not shown on the wireframe, mark it as `Not shown on wireframe`.
|
|
@@ -10,6 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
TABLE_ROW_RE = re.compile(r"^\|.*\|\s*$")
|
|
11
11
|
TABLE_SEP_RE = re.compile(r"^\|\s*:?-+:?\s*(\|\s*:?-+:?\s*)+\|\s*$")
|
|
12
12
|
HEADING_RE = re.compile(r"^(#{1,6})\s+(.*\S)\s*$")
|
|
13
|
+
SCREEN_SECTION_HEADING_RE = re.compile(r"^###\s+(.*\S)\s*$", re.MULTILINE)
|
|
13
14
|
VI_DIACRITIC_RE = re.compile(r"[À-ỹ]")
|
|
14
15
|
VI_PHRASE_RE = re.compile(
|
|
15
16
|
r"\b("
|
|
@@ -19,6 +20,15 @@ VI_PHRASE_RE = re.compile(
|
|
|
19
20
|
re.IGNORECASE,
|
|
20
21
|
)
|
|
21
22
|
INLINE_HEADING_RE = re.compile(r".+\s#{2,}\s+\S")
|
|
23
|
+
IMAGE_LINK_RE = re.compile(r"!\[[^\]]*\]\(([^)]+)\)")
|
|
24
|
+
PLANTUML_FENCE_START_RE = re.compile(r"^\s*```plantuml\s*$", re.IGNORECASE)
|
|
25
|
+
NEW_STYLE_ACTIVITY_LINE_RE = re.compile(
|
|
26
|
+
r"^\s*(start|stop|partition\s+\"|if\s*\(|fork(?:\s+again)?\b|end\s+fork\b|note\s+(?:right|left|top|bottom)\b|:[^;\n]+;)",
|
|
27
|
+
re.IGNORECASE,
|
|
28
|
+
)
|
|
29
|
+
LEGACY_ACTIVITY_START_RE = re.compile(r"\(\*\)")
|
|
30
|
+
LEGACY_EDGE_LABEL_RE = re.compile(r"-->\s*\[")
|
|
31
|
+
MIXED_ACTIVITY_ARROW_RE = re.compile(r":[^;\n]+;\s*-->")
|
|
22
32
|
|
|
23
33
|
|
|
24
34
|
@dataclass(frozen=True)
|
|
@@ -82,6 +92,134 @@ def collect_hygiene_issues(md_text: str, *, en_check: bool) -> dict[str, list[tu
|
|
|
82
92
|
return issues
|
|
83
93
|
|
|
84
94
|
|
|
95
|
+
def _is_root_relative_docs_href(href: str) -> bool:
|
|
96
|
+
normalized = href.strip().replace("\\", "/").lower()
|
|
97
|
+
if not normalized:
|
|
98
|
+
return False
|
|
99
|
+
if re.match(r"^[a-z][a-z0-9+.-]*://", normalized):
|
|
100
|
+
return False
|
|
101
|
+
if normalized.startswith(("mailto:", "tel:", "data:", "#")):
|
|
102
|
+
return False
|
|
103
|
+
return normalized.startswith(("docs/", "./docs/", "/docs/"))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def collect_screen_image_path_issues(md_text: str) -> list[tuple[int, str]]:
|
|
107
|
+
issues: list[tuple[int, str]] = []
|
|
108
|
+
lines = md_text.splitlines()
|
|
109
|
+
in_code_block = False
|
|
110
|
+
expecting_image_line = False
|
|
111
|
+
|
|
112
|
+
for idx, line in enumerate(lines, start=1):
|
|
113
|
+
stripped = line.strip()
|
|
114
|
+
if stripped.startswith("```"):
|
|
115
|
+
in_code_block = not in_code_block
|
|
116
|
+
expecting_image_line = False
|
|
117
|
+
continue
|
|
118
|
+
if in_code_block:
|
|
119
|
+
continue
|
|
120
|
+
if not stripped:
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
if stripped.lower().startswith("screen image:"):
|
|
124
|
+
expecting_image_line = stripped.lower() == "screen image:"
|
|
125
|
+
hrefs = IMAGE_LINK_RE.findall(stripped)
|
|
126
|
+
elif expecting_image_line and stripped.startswith("!["):
|
|
127
|
+
expecting_image_line = False
|
|
128
|
+
hrefs = IMAGE_LINK_RE.findall(stripped)
|
|
129
|
+
else:
|
|
130
|
+
hrefs = []
|
|
131
|
+
if not stripped.startswith("<!--"):
|
|
132
|
+
expecting_image_line = False
|
|
133
|
+
|
|
134
|
+
for href in hrefs:
|
|
135
|
+
if _is_root_relative_docs_href(href):
|
|
136
|
+
issues.append((idx, href))
|
|
137
|
+
|
|
138
|
+
return issues
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def collect_plantuml_activity_syntax_issues(md_text: str) -> list[tuple[int, str]]:
|
|
142
|
+
issues: list[tuple[int, str]] = []
|
|
143
|
+
lines = md_text.splitlines()
|
|
144
|
+
in_plantuml_block = False
|
|
145
|
+
block_start_line = 0
|
|
146
|
+
block_lines: list[str] = []
|
|
147
|
+
|
|
148
|
+
def flush_block() -> None:
|
|
149
|
+
nonlocal issues, block_lines, block_start_line
|
|
150
|
+
if not block_lines:
|
|
151
|
+
return
|
|
152
|
+
has_new_style = any(NEW_STYLE_ACTIVITY_LINE_RE.search(line) for line in block_lines)
|
|
153
|
+
if not has_new_style:
|
|
154
|
+
block_lines = []
|
|
155
|
+
block_start_line = 0
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
for offset, line in enumerate(block_lines, start=0):
|
|
159
|
+
line_no = block_start_line + offset
|
|
160
|
+
if LEGACY_ACTIVITY_START_RE.search(line):
|
|
161
|
+
issues.append(
|
|
162
|
+
(line_no, "Legacy activity start marker `(*)` is not allowed in new-style activity diagrams.")
|
|
163
|
+
)
|
|
164
|
+
if MIXED_ACTIVITY_ARROW_RE.search(line):
|
|
165
|
+
issues.append(
|
|
166
|
+
(line_no, "Do not append legacy `-->` transitions after a new-style `:Activity;` action line.")
|
|
167
|
+
)
|
|
168
|
+
elif LEGACY_EDGE_LABEL_RE.search(line):
|
|
169
|
+
issues.append(
|
|
170
|
+
(line_no, "Legacy edge labels like `--> [label]` are not allowed in new-style activity diagrams.")
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
block_lines = []
|
|
174
|
+
block_start_line = 0
|
|
175
|
+
|
|
176
|
+
for idx, line in enumerate(lines, start=1):
|
|
177
|
+
if not in_plantuml_block and PLANTUML_FENCE_START_RE.match(line):
|
|
178
|
+
in_plantuml_block = True
|
|
179
|
+
block_start_line = idx + 1
|
|
180
|
+
block_lines = []
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
if in_plantuml_block and line.strip().startswith("```"):
|
|
184
|
+
flush_block()
|
|
185
|
+
in_plantuml_block = False
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
if in_plantuml_block:
|
|
189
|
+
block_lines.append(line)
|
|
190
|
+
|
|
191
|
+
if in_plantuml_block:
|
|
192
|
+
flush_block()
|
|
193
|
+
|
|
194
|
+
return issues
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def collect_wireframe_mapping_issues(md_text: str) -> list[tuple[int, str]]:
|
|
198
|
+
issues: list[tuple[int, str]] = []
|
|
199
|
+
sections = list(SCREEN_SECTION_HEADING_RE.finditer(md_text))
|
|
200
|
+
for idx, match in enumerate(sections):
|
|
201
|
+
start = match.start()
|
|
202
|
+
end = sections[idx + 1].start() if idx + 1 < len(sections) else len(md_text)
|
|
203
|
+
section_text = md_text[start:end]
|
|
204
|
+
|
|
205
|
+
if "Design Source Type: generated-draft" not in section_text:
|
|
206
|
+
continue
|
|
207
|
+
if "> Screen image not rendered in this environment." in section_text:
|
|
208
|
+
continue
|
|
209
|
+
if "Screen image:" not in section_text or "![" not in section_text:
|
|
210
|
+
continue
|
|
211
|
+
if "#### Wireframe Marker Mapping" in section_text:
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
line_no = md_text.count("\n", 0, start) + 1
|
|
215
|
+
section_name = match.group(1).strip()
|
|
216
|
+
issues.append(
|
|
217
|
+
(line_no, f"{section_name}: missing `Wireframe Marker Mapping` table for generated-draft screen.")
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
return issues
|
|
221
|
+
|
|
222
|
+
|
|
85
223
|
def parse_action_tables(md_text: str) -> list[Occurrence]:
|
|
86
224
|
occurrences: list[Occurrence] = []
|
|
87
225
|
current_section = ""
|
|
@@ -172,9 +310,6 @@ def main() -> int:
|
|
|
172
310
|
|
|
173
311
|
text = _read_text(spec_path)
|
|
174
312
|
occ = parse_action_tables(text)
|
|
175
|
-
if not occ:
|
|
176
|
-
print("[WARN] No action tables detected (| No | Action | Description | ... |).")
|
|
177
|
-
return 0
|
|
178
313
|
|
|
179
314
|
by_number: dict[int, list[Occurrence]] = {}
|
|
180
315
|
for o in occ:
|
|
@@ -191,16 +326,28 @@ def main() -> int:
|
|
|
191
326
|
|
|
192
327
|
has_issues = bool(duplicates or decreases)
|
|
193
328
|
hygiene = collect_hygiene_issues(text, en_check=args.en_check)
|
|
329
|
+
image_path_issues = collect_screen_image_path_issues(text)
|
|
330
|
+
plantuml_activity_issues = collect_plantuml_activity_syntax_issues(text)
|
|
331
|
+
wireframe_mapping_issues = collect_wireframe_mapping_issues(text)
|
|
194
332
|
if hygiene["encoding"] or hygiene["inline_heading"]:
|
|
195
333
|
has_issues = True
|
|
196
334
|
if args.en_check and (hygiene["vi_diacritic"] or hygiene["vi_phrase"]):
|
|
197
335
|
has_issues = True
|
|
336
|
+
if image_path_issues:
|
|
337
|
+
has_issues = True
|
|
338
|
+
if plantuml_activity_issues:
|
|
339
|
+
has_issues = True
|
|
340
|
+
if wireframe_mapping_issues:
|
|
341
|
+
has_issues = True
|
|
198
342
|
|
|
199
343
|
print(f"Checked: {spec_path}")
|
|
200
344
|
print(f"- Total numbered rows: {len(occ)}")
|
|
201
345
|
print(f"- Unique numbers: {len(by_number)}")
|
|
202
346
|
print(f"- EN hygiene check enabled: {args.en_check}")
|
|
203
347
|
|
|
348
|
+
if not occ:
|
|
349
|
+
print("[WARN] No action tables detected (| No | Action | Description | ... |).")
|
|
350
|
+
|
|
204
351
|
if duplicates:
|
|
205
352
|
print("\n[FAIL] Duplicate numbers found:")
|
|
206
353
|
for n in sorted(duplicates.keys()):
|
|
@@ -233,6 +380,21 @@ def main() -> int:
|
|
|
233
380
|
for line_no, line in hygiene["vi_phrase"][:20]:
|
|
234
381
|
print(f"- line {line_no}: {line.strip()}")
|
|
235
382
|
|
|
383
|
+
if image_path_issues:
|
|
384
|
+
print("\n[FAIL] Screen image markdown paths must be file-relative, not docs-root-relative:")
|
|
385
|
+
for line_no, href in image_path_issues[:20]:
|
|
386
|
+
print(f"- line {line_no}: {href}")
|
|
387
|
+
|
|
388
|
+
if plantuml_activity_issues:
|
|
389
|
+
print("\n[FAIL] Mixed or legacy PlantUML activity syntax detected in a new-style screen-flow block:")
|
|
390
|
+
for line_no, message in plantuml_activity_issues[:20]:
|
|
391
|
+
print(f"- line {line_no}: {message}")
|
|
392
|
+
|
|
393
|
+
if wireframe_mapping_issues:
|
|
394
|
+
print("\n[FAIL] Missing wireframe-marker mapping tables for generated-draft screens:")
|
|
395
|
+
for line_no, message in wireframe_mapping_issues[:20]:
|
|
396
|
+
print(f"- line {line_no}: {message}")
|
|
397
|
+
|
|
236
398
|
if not has_issues:
|
|
237
399
|
print("\n[OK] Numbering and text hygiene checks passed.")
|
|
238
400
|
return 0
|
|
@@ -240,6 +402,9 @@ def main() -> int:
|
|
|
240
402
|
print("\nHint: Use global numbering across the full document.")
|
|
241
403
|
print("- Do not repeat No values in any action table.")
|
|
242
404
|
print("- Do not reset numbering per screen/dialog.")
|
|
405
|
+
print("- Use file-relative screen image paths such as assets/<feature>/screens/<screen>.svg.")
|
|
406
|
+
print("- Use new-style PlantUML activity syntax only for screen-flow diagrams.")
|
|
407
|
+
print("- Add a `Wireframe Marker Mapping` table when a generated-draft screen embeds a wireframe image.")
|
|
243
408
|
print("- Keep EN artifacts free of Vietnamese leftovers and encoding corruption.")
|
|
244
409
|
print("- Keep headings/sections on separate lines (avoid merged markdown blocks).")
|
|
245
410
|
return 1
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: api-design-spec
|
|
3
|
-
description: Generate detailed API design markdown from OpenAPI YAML and API flow list
|
|
3
|
+
description: Generate detailed API design markdown from OpenAPI YAML and API flow list. Use when you need field-level request/response tables plus one visible process-flow diagram per API.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Required Inputs (read before proceeding)
|
|
7
7
|
Read the following artifacts for the current feature:
|
|
8
|
-
1. `docs/api/[FeaturePascal]_API.yaml`
|
|
9
|
-
2. `docs/api/[feature_snake]_api_flow_list.txt`
|
|
8
|
+
1. `docs/api/[FeaturePascal]_API.yaml` - API contract
|
|
9
|
+
2. `docs/api/[feature_snake]_api_flow_list.txt` - flow list
|
|
10
10
|
|
|
11
11
|
## Input
|
|
12
12
|
$ARGUMENTS
|
|
13
13
|
|
|
14
14
|
# SDTK API Design Detail Spec
|
|
15
15
|
|
|
16
|
+
## Critical Constraints
|
|
17
|
+
- I do not drift from the source YAML or flow list.
|
|
18
|
+
- I do not leave broken flow image embeds in the generated markdown.
|
|
19
|
+
|
|
16
20
|
## Outputs
|
|
17
21
|
- `docs/api/[FEATURE_KEY]_API_DESIGN_DETAIL.md`
|
|
18
22
|
- Supporting generated assets:
|
|
@@ -40,15 +44,15 @@ $ARGUMENTS
|
|
|
40
44
|
## Generation Procedure
|
|
41
45
|
1. Resolve input files (`yaml`, `flow_list`, `output`).
|
|
42
46
|
2. Parse YAML endpoints (method, path, request schema, success/error schema).
|
|
43
|
-
3. Parse flow blocks from flow list
|
|
47
|
+
3. Parse flow blocks from flow list and map them by normalized `METHOD + path`.
|
|
44
48
|
4. Generate detailed markdown sections per API:
|
|
45
49
|
- Flow summary / notes / login bullets from YAML `description`
|
|
46
50
|
- Process flow source block (`text` fenced block)
|
|
47
51
|
- Embedded flowchart image
|
|
48
52
|
- Path parameter table
|
|
49
|
-
- Request table
|
|
53
|
+
- Request table
|
|
50
54
|
- Success response table
|
|
51
|
-
- Error response table
|
|
55
|
+
- Error response table
|
|
52
56
|
5. Generate/update `.puml` per API under `docs/api/flows`.
|
|
53
57
|
6. Render `.svg` images under `docs/api/images`.
|
|
54
58
|
7. Validate:
|
|
@@ -58,17 +62,27 @@ $ARGUMENTS
|
|
|
58
62
|
- markdown tables keep `No` sequential numbering
|
|
59
63
|
|
|
60
64
|
## Script
|
|
61
|
-
-
|
|
65
|
+
- `.claude/skills/api-design-spec/scripts/generate_api_design_detail.py`
|
|
62
66
|
|
|
63
67
|
### Typical command
|
|
64
68
|
```bash
|
|
65
|
-
python scripts/generate_api_design_detail.py \
|
|
69
|
+
python ".claude/skills/api-design-spec/scripts/generate_api_design_detail.py" \
|
|
66
70
|
--feature-key SCHEDULE_WHITEBOARD \
|
|
67
71
|
--yaml "docs/api/ScheduleWhiteboard_API.yaml" \
|
|
68
72
|
--flow-list "docs/api/schedule_whiteboard_api_flow_list.txt" \
|
|
69
73
|
--output "docs/api/SCHEDULE_WHITEBOARD_API_DESIGN_DETAIL.md"
|
|
70
74
|
```
|
|
71
75
|
|
|
76
|
+
### Optional subset generation
|
|
77
|
+
```bash
|
|
78
|
+
python ".claude/skills/api-design-spec/scripts/generate_api_design_detail.py" \
|
|
79
|
+
--feature-key SCHEDULE_WHITEBOARD \
|
|
80
|
+
--yaml "docs/api/ScheduleWhiteboard_API.yaml" \
|
|
81
|
+
--flow-list "docs/api/schedule_whiteboard_api_flow_list.txt" \
|
|
82
|
+
--output "docs/api/SCHEDULE_WHITEBOARD_API_DESIGN_DETAIL.md" \
|
|
83
|
+
--include "POST /api/whiteboard/assignment/{company_uuid}"
|
|
84
|
+
```
|
|
85
|
+
|
|
72
86
|
## Orchestrator Integration (Hybrid)
|
|
73
87
|
- `apiDesignDetailMode` in `sdtk.config.json` controls orchestration behavior:
|
|
74
88
|
- `auto` (default): generate API design detail when ARCH has API scope and YAML/flow sources are available.
|