@shanyucoder/flowgrid 0.1.11 → 0.1.12

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.
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Two-step audit for bundle interactionCases (see harness/docs/extracts/agent-design-context.md).
3
+ */
4
+
5
+ function countMutationActions(design) {
6
+ const actions = design?.actions
7
+ if (!Array.isArray(actions)) return 0
8
+ return actions.filter((a) => a && (a.executionContract || a.apiRef)).length
9
+ }
10
+
11
+ export function interactionCasesComplexityTriggers(bundle) {
12
+ const design = bundle?.design ?? {}
13
+ const statuses = design?.stateMatrix?.recordStatuses
14
+ const statusCount = Array.isArray(statuses) ? statuses.length : 0
15
+ const mutations = countMutationActions(design)
16
+ const scenarios = bundle?.userStories?.scenarios
17
+ const scenarioCount = Array.isArray(scenarios) ? scenarios.length : 0
18
+ const listOnly =
19
+ design?.codegen?.profile === 'list' ||
20
+ String(bundle?.gen?.codegen?.profile || '') === 'list'
21
+
22
+ return {
23
+ complex:
24
+ statusCount >= 3 ||
25
+ mutations >= 2 ||
26
+ (!listOnly && scenarioCount >= 4) ||
27
+ Boolean(design?.stateMatrix?.behaviors?.length >= 3),
28
+ statusCount,
29
+ mutations,
30
+ scenarioCount,
31
+ }
32
+ }
33
+
34
+ /**
35
+ * @param {Record<string, unknown>} bundle parsed bundle YAML
36
+ * @param {{ addGap: Function, addConfirm: Function, addWarning: Function }} hooks
37
+ */
38
+ export function auditInteractionCases(bundle, hooks) {
39
+ const { addGap, addConfirm, addWarning } = hooks
40
+ const ic = bundle?.interactionCases
41
+ const { complex, statusCount, mutations } = interactionCasesComplexityTriggers(bundle)
42
+
43
+ if (ic == null) {
44
+ if (!complex) return
45
+ addConfirm(
46
+ 'CONFIRM_INTERACTION_CASES_POLICY',
47
+ 'interactionCases',
48
+ 'Screen has complex state/actions — declare interactionCases (IC-* + sequenceDiagram)?',
49
+ [
50
+ '(Recommended) Yes — set interactionCases.policy: required and items[]',
51
+ 'No — simple screen (set interactionCases.policy: skip + skipReason)',
52
+ 'Other — note in qa/',
53
+ ],
54
+ 0,
55
+ )
56
+ return
57
+ }
58
+
59
+ const policy = ic.policy
60
+ if (policy === 'skip') {
61
+ if (!String(ic.skipReason || '').trim()) {
62
+ addWarning(
63
+ 'INTERACTION_CASES_SKIP_NO_REASON',
64
+ 'interactionCases.skipReason',
65
+ 'interactionCases.policy is skip but skipReason is empty.',
66
+ 'Declare skipReason explaining why L2 sequences are not needed.',
67
+ )
68
+ }
69
+ return
70
+ }
71
+
72
+ const items = Array.isArray(ic.items) ? ic.items : []
73
+
74
+ if (policy === 'required' && items.length === 0) {
75
+ addGap(
76
+ 'INTERACTION_CASES_ITEMS_MISSING',
77
+ 'critical',
78
+ 'interactionCases.items',
79
+ 'interactionCases.policy is required but items[] is empty.',
80
+ 'Add at least one IC-* item with description and sequenceDiagram.',
81
+ )
82
+ return
83
+ }
84
+
85
+ if (items.length === 0) return
86
+
87
+ const actionIds = new Set(
88
+ (bundle?.design?.actions || [])
89
+ .map((a) => a?.id)
90
+ .filter(Boolean),
91
+ )
92
+ const statuses = new Set(bundle?.design?.stateMatrix?.recordStatuses || [])
93
+
94
+ for (let i = 0; i < items.length; i++) {
95
+ const item = items[i]
96
+ const base = `interactionCases.items[${i}]`
97
+ if (!item?.id) {
98
+ addGap(
99
+ 'INTERACTION_CASES_MISSING_ID',
100
+ 'critical',
101
+ `${base}.id`,
102
+ 'Interaction case missing id (use IC-*).',
103
+ 'Declare id: IC-SUBMIT-HAPPY',
104
+ )
105
+ } else if (!/^IC-[A-Z0-9][A-Z0-9-]*$/i.test(String(item.id))) {
106
+ addWarning(
107
+ 'INTERACTION_CASES_ID_FORMAT',
108
+ `${base}.id`,
109
+ `Interaction case id "${item.id}" should match IC-* pattern.`,
110
+ 'Rename to IC-<DOMAIN>-<CASE> (e.g. IC-SUBMIT-409).',
111
+ )
112
+ }
113
+ if (!String(item?.title || '').trim()) {
114
+ addGap(
115
+ 'INTERACTION_CASES_MISSING_TITLE',
116
+ 'critical',
117
+ `${base}.title`,
118
+ 'Interaction case missing title.',
119
+ 'Add short title for reviewers and agents.',
120
+ )
121
+ }
122
+ if (!String(item?.description || '').trim()) {
123
+ addGap(
124
+ 'INTERACTION_CASES_MISSING_DESCRIPTION',
125
+ 'critical',
126
+ `${base}.description`,
127
+ 'Interaction case missing description (user case prose).',
128
+ 'Describe user steps and expected UI/API feedback.',
129
+ )
130
+ }
131
+ const seq = String(item?.sequenceDiagram || '')
132
+ if (!seq.includes('sequenceDiagram')) {
133
+ addGap(
134
+ 'INTERACTION_CASES_MISSING_SEQUENCE',
135
+ 'critical',
136
+ `${base}.sequenceDiagram`,
137
+ 'Interaction case missing Mermaid sequenceDiagram.',
138
+ 'Add sequenceDiagram with alt/else for error paths when applicable.',
139
+ )
140
+ } else if (!/alt\s+/i.test(seq) && complex) {
141
+ addWarning(
142
+ 'INTERACTION_CASES_NO_ALT',
143
+ `${base}.sequenceDiagram`,
144
+ 'Sequence diagram has no alt/else block — recommend at least one error branch.',
145
+ 'Model validation, 409, or timeout with alt/else.',
146
+ )
147
+ }
148
+ const actionId = item?.links?.actionId
149
+ if (actionId && actionIds.size && !actionIds.has(actionId)) {
150
+ addWarning(
151
+ 'INTERACTION_CASES_UNKNOWN_ACTION',
152
+ `${base}.links.actionId`,
153
+ `links.actionId "${actionId}" not found in design.actions.`,
154
+ 'Align with an existing action id or add the action.',
155
+ )
156
+ }
157
+ const st = item?.links?.recordStatus
158
+ if (st && statuses.size && !statuses.has(st)) {
159
+ addWarning(
160
+ 'INTERACTION_CASES_UNKNOWN_STATUS',
161
+ `${base}.links.recordStatus`,
162
+ `links.recordStatus "${st}" not in stateMatrix.recordStatuses.`,
163
+ `Use one of: ${[...statuses].join(', ')}`,
164
+ )
165
+ }
166
+ }
167
+
168
+ if (policy === 'required' && mutations >= 2 && items.length < mutations) {
169
+ addWarning(
170
+ 'INTERACTION_CASES_FEW_ITEMS',
171
+ 'interactionCases.items',
172
+ `Only ${items.length} interaction case(s) for ${mutations} mutation action(s).`,
173
+ 'Consider one IC-* per critical mutation path (happy + conflict).',
174
+ )
175
+ }
176
+
177
+ if (complex && !policy) {
178
+ addWarning(
179
+ 'INTERACTION_CASES_POLICY_UNSET',
180
+ 'interactionCases.policy',
181
+ 'interactionCases present but policy not set to required or skip.',
182
+ 'Set policy: required or policy: skip with skipReason.',
183
+ )
184
+ }
185
+ }
@@ -20,6 +20,7 @@ import {
20
20
  import { applyOpenQaField } from './open-qa.mjs'
21
21
  import { applyDesignApiFrom01 } from './hydrate-design-api.mjs'
22
22
  import { isPlaceholderLegacy, projectBusinessPage } from './project-business-layout.mjs'
23
+ import { applyInteractionCasesToIr } from './interaction-cases.mjs'
23
24
 
24
25
  export function bundlePageId(bundle) {
25
26
  const v = bundle?.['page-id'] ?? bundle?.pageId ?? bundle?.id
@@ -39,6 +40,12 @@ export function buildIrFromBundle(bundle) {
39
40
  const pageId = bundlePageId(bundle)
40
41
  const legacy = { id: pageId, ...(bundle.legacy ?? {}) }
41
42
  const design = buildDesignIr(bundle, designSpec, gen)
43
+ if (bundle.interactionCases != null) {
44
+ delete spec.interactionCases
45
+ applyInteractionCasesToIr(spec, design, bundle.interactionCases)
46
+ } else if (spec.interactionCases) {
47
+ delete spec.interactionCases
48
+ }
42
49
 
43
50
  return { spec, legacy, design, designSpec, gen }
44
51
  }
@@ -15,6 +15,7 @@ export const BUNDLE_META_KEYS = [
15
15
  'scopeIn',
16
16
  'nonGoals',
17
17
  'userFlows',
18
+ 'interactionCases',
18
19
  'nfr',
19
20
  'userStories',
20
21
  'specOrigin',
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Split bundle interactionCases → ir/spec (prose) + ir/design (sequenceDiagram only).
3
+ */
4
+
5
+ /**
6
+ * @param {unknown} interactionCases
7
+ * @returns {{ specPart: object|null, designPart: object|null }}
8
+ */
9
+ export function partitionInteractionCasesForIr(interactionCases) {
10
+ if (!interactionCases || typeof interactionCases !== 'object') {
11
+ return { specPart: null, designPart: null }
12
+ }
13
+ const { policy, skipReason, items } = interactionCases
14
+ const specPart = {
15
+ ...(policy != null ? { policy } : {}),
16
+ ...(skipReason != null && String(skipReason).trim() ? { skipReason } : {}),
17
+ items: Array.isArray(items)
18
+ ? items.map((item) => {
19
+ if (!item || typeof item !== 'object') return item
20
+ const { id, title, description, links } = item
21
+ return { id, title, description, links }
22
+ })
23
+ : [],
24
+ }
25
+ const designItems = Array.isArray(items)
26
+ ? items
27
+ .filter((item) => item && typeof item === 'object' && item.sequenceDiagram)
28
+ .map(({ id, sequenceDiagram }) => ({ id, sequenceDiagram }))
29
+ : []
30
+ const designPart = designItems.length ? { items: designItems } : null
31
+ return { specPart, designPart }
32
+ }
33
+
34
+ /**
35
+ * @param {Record<string, unknown>} spec
36
+ * @param {Record<string, unknown>} design
37
+ * @param {unknown} interactionCases from bundle
38
+ */
39
+ export function applyInteractionCasesToIr(spec, design, interactionCases) {
40
+ const { specPart, designPart } = partitionInteractionCasesForIr(interactionCases)
41
+ if (!specPart && !designPart) return
42
+ if (specPart && (specPart.policy || specPart.skipReason || specPart.items?.length)) {
43
+ spec.interactionCases = specPart
44
+ }
45
+ if (designPart) design.interactionCases = designPart
46
+ }
@@ -34,7 +34,7 @@ async function main() {
34
34
  const mdRel = mdOut ? path.relative(process.cwd(), mdOut) : null
35
35
  const genDir = mdOut ? path.dirname(mdOut) : null
36
36
  const extra = genDir
37
- ? ['data-model.md', 'api.md']
37
+ ? ['data-model.md', 'api.md', 'design.md']
38
38
  .filter((name) => fs.existsSync(path.join(genDir, name)))
39
39
  .map((name) => path.relative(process.cwd(), path.join(genDir, name)))
40
40
  : []
@@ -0,0 +1,124 @@
1
+ # Agent design context (L1 FLOW · L2 interactionCases · NFR · ACP)
2
+
3
+ **Status:** Implemented — `interactionCases`, split/render, audit, ACP skills, `screenToFlows`, MCP `linkedFlows` / `flowgrid_docs_user_flows?id=`.
4
+
5
+ **See also:** `product-id-convention.md`. BE runtime-sequence diagram layer is a future phase (not L1/L2).
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ Agents often read only `*.bundle.yaml` + `ir/design.yaml` and miss three independent axes:
12
+
13
+ | Axis | SSOT | Agent gap |
14
+ | --- | --- | --- |
15
+ | **L1 Journey (cross-screen)** | `FLOW-*.md` (Markdown only) | No enforced read before codegen on `W-*` |
16
+ | **L2 Interaction (one screen)** | `interactionCases` on bundle + `stateMatrix` / `actions` | No timeline diagram per branch (409, double-submit, …) |
17
+ | **NFR overlay** | `bundle.nfr` + `architecture/08-*` links | NFR treated as appendix, not constraint |
18
+
19
+ **Sequence diagrams at the right layer materially improve codegen quality.**
20
+
21
+ ---
22
+
23
+ ## Layer model
24
+
25
+ | Layer | SSOT | Format | Question answered |
26
+ | --- | --- | --- | --- |
27
+ | **L1** | `FLOW-*` · `common/user-flows/` | MD + **§6 `sequenceDiagram`** | Where the user goes across screens |
28
+ | **L2** | `interactionCases` on bundle (optional) | YAML → split | One `W-*`: cases + per-case sequence |
29
+ | **L3** | `design.stateMatrix` | YAML | Machine state/button matrix |
30
+ | **L4** | bundle + `ir/design.yaml` + `01` | YAML | Fields, actions, apiRef, outcomes |
31
+ | **NFR** | `bundle.nfr` | MD bullets | Constraints on all cases |
32
+
33
+ **Do not** put L2 single-screen branches into FLOW MD. **Do not** require L2 on trivial list-only GET screens.
34
+
35
+ ---
36
+
37
+ ## L1 ↔ leaf link: `userFlows` on bundle
38
+
39
+ ```yaml
40
+ userFlows: |
41
+ - FLOW-checkout — role: step-3-ops — screens: W-ADM-ORD-01 (this leaf)
42
+ ```
43
+
44
+ | Token | Agent use |
45
+ | --- | --- |
46
+ | `FLOW-*` | Open FLOW MD SSOT |
47
+ | `role` | Focus §4 stage |
48
+ | `screens` + `(this leaf)` | Filter journey to current screen |
49
+
50
+ Resolve FLOW paths: `architecture/03-user-flows/<FLOW>.md`, `surfaces/**/common/user-flows/<FLOW>.md`.
51
+
52
+ **Discovery:** `flowgrid_docs_route("W-…")` → `linkedFlows[]`; index `registries/docs-index.json` → `screenToFlows`.
53
+
54
+ ---
55
+
56
+ ## L2: `interactionCases` on bundle
57
+
58
+ Authoring SSOT: `feature.bundle.yaml` (not separate FLOW files).
59
+
60
+ ```yaml
61
+ interactionCases:
62
+ policy: required | skip
63
+ skipReason: "" # required when policy: skip
64
+ items:
65
+ - id: IC-SUBMIT-409
66
+ title: ...
67
+ description: ...
68
+ sequenceDiagram: |
69
+ sequenceDiagram
70
+ ...
71
+ ```
72
+
73
+ **Split:** prose → `ir/spec.yaml` → `ir/generated/spec.md` (§ Interaction cases); diagrams only → `ir/design.yaml` → `ir/generated/design.md`.
74
+
75
+ **Codegen read order for L2:** `spec.md` prose + `design.md` mermaid + `stateMatrix` / `actions` — not FLOW.
76
+
77
+ ---
78
+
79
+ ## Agent context package (ACP) — read before codegen
80
+
81
+ For target leaf `W-*`:
82
+
83
+ 1. **`userFlows`** → each **`FLOW-*.md`** (§3–§4 for this screen, **§6 sequence**).
84
+ 2. **`bundle.nfr`** (+ linked `architecture/08-cross-cutting/*` when cited).
85
+ 3. **`ir/generated/design.md`** when `interactionCases.policy: required` (after split + render).
86
+ 4. **`ir/design.yaml`** + **`01-backend-spec.yaml`**.
87
+ 5. Then patch `bundle.gen` / run gen.
88
+
89
+ **Chat checklist:** FLOW ids read · NFR read · L2 design.md yes/no.
90
+
91
+ **Skills:** `/spec` (declare `userFlows` when cross-screen); `/grill-dev`, `/prototype`, `/api-spec` (ACP); `/user-flow` (author FLOW + §6, trace `W-*` in §5).
92
+
93
+ ---
94
+
95
+ ## Audit (`flowgrid audit spec`)
96
+
97
+ **Step 1 — policy confirm** when screen is complex and `interactionCases` missing:
98
+
99
+ - Emit `CONFIRM_INTERACTION_CASES_POLICY` in `confirms[]`.
100
+ - Options: **(Recommended) required** — set `policy: required` + `items[]`; **skip** — `policy: skip` + `skipReason`; **Other** (free text).
101
+
102
+ **Step 2 — item validation** when `policy: required` or `items.length > 0`:
103
+
104
+ - Each item needs `id`, `title`, `description`, `sequenceDiagram`.
105
+ - Optional `links.actionId`, `links.recordStatus`, `links.scenario`.
106
+
107
+ Skip step 1 when `interactionCases.policy: skip` is already declared.
108
+
109
+ ---
110
+
111
+ ## VitePress (member review)
112
+
113
+ | Page | Source |
114
+ | --- | --- |
115
+ | Spec | `ir/generated/spec.md` |
116
+ | Linked flows | Rendered from `userFlows` |
117
+ | Design sequences | `ir/generated/design.md` |
118
+ | API | `ir/generated/api.md` |
119
+
120
+ ---
121
+
122
+ ## One-line summary
123
+
124
+ **L1:** `/user-flow` → `FLOW-*.md` + §6; leaf links via **`userFlows`**. **NFR:** mandatory read before design/API. **L2:** **`interactionCases`** on bundle → `spec.md` + `design.md`; never in FLOW.
@@ -28,12 +28,14 @@
28
28
  ".cursor/extracts/db-audit-wizard.md",
29
29
  ".cursor/extracts/agent-execution-protocol.md",
30
30
  ".cursor/extracts/common-scope.md",
31
- ".cursor/extracts/product-id-convention.md"
31
+ ".cursor/extracts/product-id-convention.md",
32
+ ".cursor/extracts/agent-design-context.md"
32
33
  ],
33
34
  "spec-core": [
34
35
  ".cursor/extracts/spec-core.md",
35
36
  ".cursor/extracts/common-scope.md",
36
- ".cursor/extracts/product-id-convention.md"
37
+ ".cursor/extracts/product-id-convention.md",
38
+ ".cursor/extracts/agent-design-context.md"
37
39
  ],
38
40
  "bqa-grill": [
39
41
  ".cursor/extracts/grill/validation.md",
@@ -43,7 +45,8 @@
43
45
  "dev-grill": [
44
46
  ".cursor/extracts/codegen/readiness.md",
45
47
  ".cursor/extracts/docs-mark-detect.md",
46
- ".cursor/extracts/db-audit-wizard.md"
48
+ ".cursor/extracts/db-audit-wizard.md",
49
+ ".cursor/extracts/agent-design-context.md"
47
50
  ],
48
51
  "grill-docs": [
49
52
  ".cursor/extracts/grill-docs-reconcile.md",
@@ -18,6 +18,10 @@ Shared extracts: `spec-evolution.md`, `api-spec-sync.md`, `entity-relationship.m
18
18
 
19
19
  Hashtag extracts: `#call-external` → `call-external.md`; `#cross-entity-service` → `cross-entity-service.md`.
20
20
 
21
+ ## Agent context (ACP) before `01`
22
+
23
+ Per **`agent-design-context.md`**: (1) `userFlows` → `FLOW-*.md` §6 (`flowgrid_docs_user_flows` / `flowgrid_docs_route`); (2) `bundle.nfr`; (3) `ir/generated/design.md` if `interactionCases.policy: required`; (4) `ir/design.yaml` + `01`. Print checklist: FLOW · NFR · L2.
24
+
21
25
  ---
22
26
 
23
27
  ## Rule: Audit Interlock
@@ -28,25 +28,38 @@ disable-model-invocation: true
28
28
 
29
29
  ---
30
30
 
31
+ ## Rule: Agent context (ACP) before codegen
32
+
33
+ Per **`agent-design-context.md`** — read **before** patching `bundle.gen` / codegen:
34
+
35
+ 1. **`userFlows`** → each linked **`FLOW-*.md`** (§3–§4 for this `W-*`, **§6 sequence**).
36
+ 2. **`bundle.nfr`** (+ linked `architecture/08` if cited).
37
+ 3. **`ir/generated/design.md`** when `interactionCases.policy: required` (after `split` + `render`).
38
+ 4. Then **`ir/design.yaml`** + **`01`**.
39
+
40
+ Print checklist in chat: FLOW ids read · NFR read · L2 design.md yes/no.
41
+
42
+ ---
43
+
31
44
  ## Rule: Audit Interlock with Page Type
32
45
 
33
46
  - **[MANDATORY]** Before grilling, run: `flowgrid audit spec <bundle> --type <profile>`. Treat `warnings[]` as non-blocking; nudge BQA via handoff if summary still has `[placeholder]` brackets.
34
47
  - **[MANDATORY]** After `bundle.gen` patch: `flowgrid split` + `flowgrid render` (FE uses `ir/design.yaml`; BA PDF path uses `ir/generated/spec.md`).
35
- - `<profile>` lấy từ `gen.codegen.profile` đã xác nhận (list | create | detail | admin-crud | auth | ...).
36
- - Nếu profile chưa set → hỏi member xác định profile trước, KHÔNG chạy audit với `--type unknown`.
48
+ - `<profile>` comes from confirmed `gen.codegen.profile` (list | create | detail | admin-crud | auth | ...).
49
+ - If profile is unset → ask the member to confirm profile first; do **not** run audit with `--type unknown`.
37
50
  - Script output `gaps[]` + `confirms[]` (`CONFIRM_UX_*`, **`CONFIRM_DB_*`** — `db-audit-wizard.md`).
38
51
  - Agent patches structural `gaps[]`; UX/DB confirms → wizard; BE drift → `/api-update` then re-audit.
39
52
 
40
53
  ---
41
54
 
42
- ## Rule: Zone-Based Grill (chống Lost-in-Middle)
55
+ ## Rule: Zone-Based Grill (avoid lost-in-the-middle)
43
56
 
44
- - **[MANDATORY]** Grill theo zone, KHÔNG grill toàn bộ bundle 1 lần.
45
- - **[MANDATORY]** Chia bundle thành zones linh động theo nội dung thực:
46
- - Mỗi turn grill 1 zone: đọc zone data → phân tích chất (logic, consistency, cross-field gaps) → bổ sung/sửa.
47
- - Nếu zone quá lớn → chia nhỏ tiếp.
48
- - **[MANDATORY]** Script check **lượng** (field có/không). Agent check **chất** (nội dung chuẩn, hợp logic, gaps giữa fields).
49
- - **[STRICTLY FORBIDDEN]** Gửi all-in-one rồi bỏ sót giữa.
57
+ - **[MANDATORY]** Grill by zone; do **not** grill the entire bundle in one pass.
58
+ - **[MANDATORY]** Split the bundle into zones based on actual content:
59
+ - One zone per turn: read zone data → analyze quality (logic, consistency, cross-field gaps) → patch.
60
+ - If a zone is too large → subdivide further.
61
+ - **[MANDATORY]** Script checks **quantity** (field present or not). Agent checks **quality** (correct content, logic, cross-field gaps).
62
+ - **[STRICTLY FORBIDDEN]** Single all-in-one pass that drops items in the middle.
50
63
 
51
64
  ## Rule: Missing Information / Hard Gate & Workload Threshold (Law 2)
52
65
 
@@ -9,7 +9,7 @@ disable-model-invocation: true
9
9
  > **[MANDATORY]** Re-read this entire `SKILL.md` via file-read tool. **STRICTLY FORBIDDEN** to rely on memory.
10
10
  > **[MANDATORY]** Read `.flowgrid/templates/feature.bundle.yaml` + `.flowgrid/templates/bundle-authoring.md` BEFORE generating any YAML. **Do NOT** use `design-spec.yaml` (deprecated).
11
11
  > If templates are missing → STOP: *"Template missing. Run `flowgrid init` to generate templates."*
12
- > **Extract:** `spec-ssot-prep.md`, `spec-prd-lite.md`, `db-audit-wizard.md`, `design-leaf-signoff.md`.
12
+ > **Extract:** `spec-ssot-prep.md`, `spec-prd-lite.md`, `db-audit-wizard.md`, `design-leaf-signoff.md`, `agent-design-context.md`.
13
13
  > Physical interlocks: `AGENTS.md` + `SSOT_AGENT_PROTOCOL.md` (Laws 1–7). Chat-only done = **FAILED**.
14
14
 
15
15
  # /spec — Function detail (design)
@@ -62,7 +62,7 @@ Hub: [spec-ssot-prep.md](../../../docs/workflows/spec-ssot-prep.md) · extract `
62
62
  - `<pageType>` = page type đã xác định ở bước trên (list | create | detail | admin-crud | auth | ...).
63
63
  - Script output:
64
64
  - `gaps[]` → required fields missing → Agent patches bundle directly.
65
- - `warnings[]` → **quality hints** (`scopeIn`, `nonGoals`, `nfr`, `userFlows`, placeholders) — fix when info exists; **does not** block split. Rủi ro → `/risk-register` only (`WARN_BUNDLE_RISKS_FORBIDDEN` if `risks:` on bundle).
65
+ - `warnings[]` → **quality hints** (`scopeIn`, `nonGoals`, `nfr`, `userFlows`, placeholders) — fix when info exists; **does not** block split. Risks → `/risk-register` only (`WARN_BUNDLE_RISKS_FORBIDDEN` if `risks:` on bundle).
66
66
  - `confirms[]` → AskQuestion wizard (one question at a time, ≥3 options):
67
67
  - UX: `category: ux`, `UX_*`, `CONFIRM_UX_*` — `flowgrid-ux-common.mdc`
68
68
  - **DB:** `category: db`, `CONFIRM_DB_*` — `.cursor/extracts/db-audit-wizard.md` (entities, multi-table, `db` vs `01`, `#derived-data`)
@@ -149,11 +149,14 @@ Each zone turn — **in order**:
149
149
  - **[MANDATORY]** List columns: `key` consistent with `bind.field`; computed-only columns → document `#derived-data` (see `common/data-model/derived-data.md`), no fake `db`.
150
150
  - **[STRICTLY FORBIDDEN]** Empty `entities: []` while form/list has multiple persisted `db.field` without QA defer.
151
151
  - **Authoring detail:** [bundle-authoring.md § Data model](../../../templates/shared/bundle-authoring.md#data-model--phase-0-erd-vs-screen-detail) · [tpl-screen-data-model.md](../../../templates/shared/tpl-screen-data-model.md) (multi-table).
152
- - **Sau split:** `ir/generated/data-model.md` cho review. **Audit:** `CONFIRM_DB_*` → member wizard; sau chốt → re-audit. BE SSOT: `01` khớp `db` (drift → `/api-update`).
152
+ - **After split:** `ir/generated/data-model.md` for review. **Audit:** `CONFIRM_DB_*` → member wizard; re-audit after confirm. BE SSOT: `01` must match `db` (drift → `/api-update`).
153
153
 
154
154
  ### Rule: Summary extensions (PRD lite)
155
155
  - **[MANDATORY]** `summary` bullets: business_goals, stakeholders, user_journey, context (input/output), optional solution.
156
- - **[RECOMMENDED]** Fill `scopeIn`, `nonGoals`, `userFlows`, `nfr` when PO/BA có thông tin. Rủi ro dự án → **`/risk-register`** (`architecture/11-risks/risk-register.md`), never `risks:` on bundle.
156
+ - **[RECOMMENDED]** Fill `scopeIn`, `nonGoals`, `userFlows`, `nfr` when PO/BA provide facts. Complex single-screen flows → `interactionCases` (L2) or resolve audit `CONFIRM_INTERACTION_CASES_POLICY`. Project risks → **`/risk-register`**, never `risks:` on bundle.
157
+ - **[MANDATORY]** `userFlows:` bullets link **`FLOW-*`** (`FLOW-id — role: … — screens: W-* (this leaf)`). Journey sequence lives in **`FLOW-*.md` §6** — not in bundle.
158
+ - **[RECOMMENDED]** Multi-state / multi-case on **one screen:** optional top-level **`interactionCases`** (`policy`, `items[]` with `IC-*`, `description`, `sequenceDiagram`) — see `agent-design-context.md`. After split: prose → `spec.md`, diagrams → `design.md`.
159
+ - **[MANDATORY]** Consume `confirms[]` **`CONFIRM_INTERACTION_CASES_POLICY`** from audit — set `policy: required` or `policy: skip` + `skipReason`.
157
160
  - **[MANDATORY]** Replace template `[placeholder]` brackets in `summary` / metrics / non-goals before handoff grill.
158
161
 
159
162
  ### Rule: User Stories (`userStories`)
@@ -269,6 +272,6 @@ Each zone turn — **in order**:
269
272
  - [ ] UX gap questions used checklist-backed `(Recommended)` options (`flowgrid-ux-common.mdc`), not open brainstorming.
270
273
  - [ ] `userStories` scenarios/AC reflect UX affordances patched in `design` (incl. audit `suggestedStoryPatch`).
271
274
  - [ ] YAML strings with `:` or `[]` are double-quoted. No `.md` written by hand.
272
- - [ ] PRD fields (`scopeIn`, `nonGoals`, `userFlows`, `nfr`) filled or deferred via `qa/` (no template brackets). Rủi ro không trên bundle.
275
+ - [ ] PRD fields (`scopeIn`, `nonGoals`, `userFlows`, `nfr`) filled or deferred via `qa/` (no template brackets). No project risks on bundle (`/risk-register` only).
273
276
  - [ ] `pnpm docs:split` + `pnpm docs:render` run with zero errors; `ir/generated/spec.md` has TOC + overview sections.
274
277
  - [ ] Handoff → `/testcase` created.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: user-flow
3
- description: /user-flow — Luồng người dùng (FLOW-*) trên surfaces.
3
+ description: /user-flow — Author cross-surface user journeys (FLOW-*.md) on the docs hub.
4
4
  disable-model-invocation: true
5
5
  extractBundle: architecture-core
6
6
  ---
@@ -12,7 +12,9 @@ extractBundle: architecture-core
12
12
 
13
13
  **Mindset:** Model the process by **business actions on surfaces**, not by repository or service topology.
14
14
 
15
- **Template:** `architecture/03-user-flows/FLOW-template.md` (or `**/common/user-flows/FLOW-*.md`) — **Non-goals** in §1 when information exists (KPI ngoài hub).
15
+ **Template:** `architecture/03-user-flows/FLOW-template.md` (or `**/common/user-flows/FLOW-*.md`) — **Non-goals** in §1 when information exists (out-of-hub KPIs).
16
+
17
+ **Leaf link:** Bundles reference this file via `userFlows:` (`FLOW-* — role — screens`). Agents read FLOW **before** codegen (`agent-design-context.md`). **Do not** put single-screen multi-state cases here — use bundle `interactionCases` (L2).
16
18
 
17
19
  ---
18
20
 
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: prototype
3
- description: /prototype — UI from FLOWGRID_DOCS_ROOT ir/design.yaml with mock API via bộ code.
3
+ description: /prototype — UI from FLOWGRID_DOCS_ROOT ir/design.yaml with mock API via the FE code repo.
4
4
  disable-model-invocation: true
5
5
  ---
6
6
 
7
7
  # /prototype — UI Prototype (Mock API Boundary)
8
8
 
9
- **Owner:** bộ code (`--type=fe`) · Adapters: `nuxt4` | `nextjs` | `dotnet-line`
9
+ **Owner:** FE code repo (`--type=fe`) · Adapters: `nuxt4` | `nextjs` | `dotnet-line`
10
10
 
11
11
  ## Artifact & Target ID Resolution Rule
12
12
 
@@ -23,6 +23,17 @@ surfaces/<surface>/CMP-*/<NN…>/ir/design.yaml
23
23
 
24
24
  Read the **entire** `ir/design.yaml` (script + agent inspection). Do not filter keys from `*.bundle.yaml`. **`ir/spec.yaml`** is business prose only.
25
25
 
26
+ ## Agent context (ACP) before gen
27
+
28
+ Per **`agent-design-context.md`** (resolve `W-*` via `flowgrid_docs_route` → `linkedFlows[]`):
29
+
30
+ 1. **`userFlows` / linked `FLOW-*.md`** — §6 sequence for journey context.
31
+ 2. **`bundle.nfr`** (from bundle or `ir/spec.yaml` mirror).
32
+ 3. **`ir/generated/design.md`** when `interactionCases.policy: required`.
33
+ 4. Then **`ir/design.yaml`**.
34
+
35
+ Print checklist in chat: FLOW ids read · NFR · L2 design.md yes/no.
36
+
26
37
  **Docs hub is read-only for this skill.** Never Write/patch `*.bundle.yaml`, `ir/*`, or any file under `FLOWGRID_DOCS_ROOT`. Missing `ir/design.yaml`, empty `codegen.profile`, or `flowgrid gen` failure → **STOP**, quote the CLI error, hand off to docs-hub `/grill-dev` (or `/spec`). Do not invent SSOT to make gen pass. Login/forgot/reset must be `codegen.profile: auth` (not `create`) or gen writes `(dashboard)`.
27
38
 
28
39
  Do not invent sibling docs-hub paths. Pass `FLOWGRID_DOCS_ROOT` or `--docs-root`.
@@ -38,7 +49,7 @@ Do not invent sibling docs-hub paths. Pass `FLOWGRID_DOCS_ROOT` or `--docs-root`
38
49
 
39
50
  ## Route
40
51
 
41
- Architecture/C4 → bộ docs (`FLOWGRID_DOCS_ROOT`); IR/registry/gen →
52
+ Architecture/C4 → docs hub (`FLOWGRID_DOCS_ROOT`); IR/registry/gen →
42
53
  `FLOWGRID_DOCS_ROOT`; symbols/call-graph for repo X → Platform DNA-wired
43
54
  `codegraph-<repo-key>`. Never workspace-parent graphs, sibling-path inference,
44
55
  or member-edited MCP. Local ArtifactGraph is allowlist/tag hints for this repo
@@ -75,7 +86,7 @@ For each HANDOFF / `#needs-component: Mo…` after gen:
75
86
  4. Re-run `flowgrid gen` so slots bind to the new file.
76
87
  5. Dotnet-line / no `components.json`: skip this section; do not invent React/shadcn APIs.
77
88
 
78
- Do not copy the shadcn skill into bộ docs. Do not author `#ui:` tags from memory if `shadcn search` can name the registry item.
89
+ Do not copy the shadcn skill into the docs hub. Do not author `#ui:` tags from memory if `shadcn search` can name the registry item.
79
90
 
80
91
  `dotnet-line` requires the .NET 8 SDK (`FLOWGRID_DOTNET`, then `dotnet`) and
81
92
  is limited to the pilot-specific `kiosk-check-in` profile. Its main pass also
@@ -88,14 +99,15 @@ if local ArtifactGraph available: recommend/check the FE repo's allowlisted gen
88
99
  else: run flowgrid gen:dry / gen directly
89
100
 
90
101
  Missing ArtifactGraph never blocks prototype generation. Complete the direct,
91
- deterministic bộ code fallback first, then follow
102
+ deterministic FE-repo fallback first, then follow
92
103
  `.cursor/rules/flowgrid-code-optional-integrations.mdc` for once-per-run telemetry.
93
104
  ```
94
105
 
95
- Docs render / `spec:split` remain flowgrid / bộ docs handoffs.
106
+ Docs render / `spec:split` remain flowgrid / docs-hub handoffs.
96
107
 
97
108
  ## Translation Rule
98
- Luôn bọc text tĩnh bằng i18n helper native của framework.
99
- - Đọc block `i18n` từ `ir/design.yaml`.
100
- - Tự động sinh/cập nhật file ngôn ngữ tương ứng (`.json` cho FE/NodeJS, hoặc `.resx` cho Dotnet).
101
- - KHÔNG viết gộp ngôn ngữ lên giao diện (ví dụ không dùng `login / đăng nhập`).
109
+
110
+ - Wrap all static UI copy with the framework’s native i18n helper.
111
+ - Read the `i18n` block from `ir/design.yaml`.
112
+ - Generate or update locale files (`.json` for FE/Node, `.resx` for dotnet-line).
113
+ - Do **not** hard-code mixed languages in UI (e.g. avoid `login / đăng nhập` in one string).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shanyucoder/flowgrid",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Unified Local MCP Toolkit (Graph, DNA, Docs, Test, Codegen)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -10,7 +10,8 @@ Hub: `docs/templates/feature.bundle.yaml` · split: `pnpm spec:split`
10
10
  | `summary` | Phải trình bày dạng bullet. Bắt buộc có các tiêu đề (chuẩn Arc42 business): **mục tiêu nghiệp vụ** (business_goals), **các bên liên quan** (stakeholders), **kịch bản người dùng** (user_journey), **bối cảnh** (description, input liên kết cross-page/module, output) và **cách giải quyết** (tùy chọn). Mục đích để 100% Non-tech Stakeholder hiểu và duyệt. |
11
11
  | `scopeIn` | **Khuyến nghị** — trong phạm vi màn/phase (PRD). Audit `WARN_NO_SCOPE_IN`. |
12
12
  | `nonGoals` | **Khuyến nghị** — ngoài phạm vi màn/phase (PRD). Audit `WARN_NO_NON_GOALS`. |
13
- | `userFlows` | **Khuyến nghị** — link `FLOW-*` (`03-user-flows` / `common/user-flows`) hoặc journey ngắn. Audit `WARN_NO_USER_FLOWS` khi multi-screen. |
13
+ | `userFlows` | **Recommended** — `FLOW-* — role: … — screens: W-* (this leaf)`; agents read linked `FLOW-*.md` §6 before codegen (`agent-design-context.md`). |
14
+ | `interactionCases` | **Optional L2** — `policy` + `items[]` (`IC-*`, `description`, `sequenceDiagram`); split → `spec.md` + `ir/generated/design.md`. Audit `CONFIRM_INTERACTION_CASES_POLICY`. |
14
15
  | `nfr` | **Khuyến nghị** — hiệu năng, bảo mật. Audit `WARN_NO_NFR`. |
15
16
  | _(rủi ro)_ | **Không** khai báo trên bundle. SSOT: `architecture/11-risks/risk-register.md` — `/risk-register`. Key `risks:` → audit `WARN_BUNDLE_RISKS_FORBIDDEN`. |
16
17
  | `userStories` | **Khối User Stories chuyên sâu cho màn hình:** `primary`, `contextAndHandoff` (+ `screenAccess`), `scenarios` (5 kịch bản chuẩn + **scenario thứ 6 “Affordances UX”** khi màn có delete/filter/breadcrumb/disabled/import — xem `feature.bundle.yaml`), `acceptanceCriteria` (kèm checkbox UX khi áp dụng). **Split:** `pnpm spec:split` copy nguyên khối sang `ir/spec.yaml` (business prose); **không** tự sinh từ `design` — Agent phải cập nhật `userStories` khi bổ sung DSL/`#needs-component`/audit `UX_*`. Render → `## User Stories & Screen Journey` trong Markdown. |