@shanyucoder/flowgrid 0.1.8 → 0.1.10
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/bin/flowgrid.mjs +43 -9
- package/bin/lib/audit-run.mjs +5 -1
- package/bin/lib/docs-hub-locale.mjs +9 -0
- package/bin/lib/init-scaffold.mjs +30 -2
- package/dist/docs/mcp/tools.js +4 -4
- package/dist/docs/mcp/tools.js.map +1 -1
- package/dist/docs/scan/ids.d.ts +1 -1
- package/dist/docs/scan/ids.js +6 -6
- package/dist/docs/scan/ids.js.map +1 -1
- package/dist/docs/scan/route.js +2 -2
- package/dist/docs/scan/route.js.map +1 -1
- package/engines/cases/render-cases.mjs +33 -25
- package/engines/docs/lib/audit-hub-prd.mjs +136 -0
- package/engines/docs/lib/audit-risks-catalog.mjs +142 -0
- package/engines/docs/lib/docs-hub-locale.mjs +100 -0
- package/engines/docs/lib/qa-item.mjs +91 -0
- package/engines/docs/lib/render-bundle-markdown.mjs +8 -1
- package/engines/docs/lib/render-qa-list.mjs +123 -25
- package/engines/docs/vitepress/config.ts +4 -4
- package/engines/spec/lib/audit-bundle-gaps.mjs +38 -7
- package/engines/spec/lib/audit-flow-gaps.mjs +2 -2
- package/engines/spec/lib/bundle-schema.mjs +3 -1
- package/engines/spec/lib/open-qa.mjs +71 -21
- package/engines/testcase/runners/lib/resolve-hub-id.mjs +3 -3
- package/harness/common/skills/legacy/SKILL.md +2 -2
- package/harness/docs/extracts/agent-execution-protocol.md +2 -2
- package/harness/docs/extracts/api-codegen-readiness.md +1 -1
- package/harness/docs/extracts/api-spec-sync.md +1 -1
- package/harness/docs/extracts/call-external.md +1 -1
- package/harness/docs/extracts/common-scope.md +8 -8
- package/harness/docs/extracts/design-leaf-signoff.md +2 -2
- package/harness/docs/extracts/extract-registry.docs.json +2 -1
- package/harness/docs/extracts/qa-inbox.md +19 -10
- package/harness/docs/extracts/qa-team.md +32 -0
- package/harness/docs/extracts/spec-core.md +1 -1
- package/harness/docs/extracts/spec-evolution.md +1 -1
- package/harness/docs/extracts/spec-prd-lite.md +11 -13
- package/harness/docs/extracts/tpl-module.md +9 -40
- package/harness/docs/extracts/tpl-overview-prd.md +11 -0
- package/harness/docs/extracts/tpl-risk-register.md +28 -0
- package/harness/docs/extracts/tpl-surface-prd.md +7 -0
- package/harness/docs/rules/docs-hub.mdc +1 -1
- package/harness/docs/rules/flowgrid-process.mdc +1 -1
- package/harness/docs/schemas/flowgrid-docs/qa-item.schema.json +65 -0
- package/harness/docs/skills/adopt/SKILL.md +1 -1
- package/harness/docs/skills/api-spec/SKILL.md +1 -1
- package/harness/docs/skills/api-update/SKILL.md +1 -1
- package/harness/docs/skills/background-logic/SKILL.md +1 -1
- package/harness/docs/skills/common-spec/SKILL.md +1 -1
- package/harness/docs/skills/cross-service/SKILL.md +1 -1
- package/harness/docs/skills/db-erd/SKILL.md +1 -1
- package/harness/docs/skills/grill/SKILL.md +2 -0
- package/harness/docs/skills/grill-bqa/SKILL.md +2 -2
- package/harness/docs/skills/grill-dev/SKILL.md +1 -1
- package/harness/docs/skills/grill-docs/SKILL.md +2 -2
- package/harness/docs/skills/grill-hub-prd/SKILL.md +38 -0
- package/harness/docs/skills/module/SKILL.md +8 -5
- package/harness/docs/skills/overview/SKILL.md +7 -6
- package/harness/docs/skills/qa-resolve/SKILL.md +13 -12
- package/harness/docs/skills/qa-review/SKILL.md +45 -0
- package/harness/docs/skills/risk-register/SKILL.md +29 -0
- package/harness/docs/skills/spec/SKILL.md +4 -4
- package/harness/docs/skills/surfaces/SKILL.md +3 -1
- package/harness/docs/skills/update-spec/SKILL.md +2 -2
- package/harness/docs/skills/{business-process → user-flow}/SKILL.md +8 -8
- package/harness/fe/extracts/wire-audit-loop.md +1 -1
- package/harness/fe/skills/gen-common/SKILL.md +1 -1
- package/harness/fe/skills/grill-wire/SKILL.md +1 -1
- package/harness/fe/skills/wire/SKILL.md +1 -1
- package/harness/tests/extracts/grill-scenario-flow.md +2 -2
- package/harness/tests/skills/grill-testcase/SKILL.md +1 -1
- package/harness/tests/skills/scenario/SKILL.md +11 -11
- package/harness/tests/skills/testcase/SKILL.md +1 -1
- package/harness/tests/templates/SC.example.md +16 -16
- package/lexicon/registry-tags.en.txt +2 -2
- package/package.json +1 -1
- package/templates/project-skeleton/architecture/03-business-process/index.md +3 -0
- package/templates/project-skeleton/architecture/{03-business-process → 03-user-flows}/FLOW-login.md +6 -6
- package/templates/project-skeleton/architecture/{03-business-process → 03-user-flows}/FLOW-template.md +6 -7
- package/templates/project-skeleton/architecture/11-risks/index.md +7 -17
- package/templates/project-skeleton/architecture/11-risks/risk-register.md +34 -0
- package/templates/project-skeleton/architecture/12-glossary/index.md +2 -1
- package/templates/project-skeleton/overview/index.md +21 -43
- package/templates/project-skeleton/overview/operational-areas/_template.md +15 -22
- package/templates/project-skeleton/qa/README.md +4 -8
- package/templates/project-skeleton/surfaces/_module-index.template.md +40 -0
- package/templates/project-skeleton/surfaces/_surface-index.template.md +43 -0
- package/templates/schemas/qa-item.schema.json +98 -0
- package/templates/shared/bundle-authoring.md +10 -6
- package/templates/shared/default-layout.ejs +108 -62
- package/templates/shared/feature.bundle.yaml +15 -6
- package/templates/shared/ir/generated/spec.md +24 -24
- package/templates/shared/ir-spec.yaml +1 -1
- package/templates/shared/qa-authoring.md +78 -0
- package/templates/shared/qa-item.yaml +35 -14
- package/templates/shared/tpl-api-contract.md +6 -6
- package/templates/tests-skeleton/catalog/locale.yaml +16 -15
- package/templates/tests-skeleton/tpl-testcase-plan.md +6 -6
|
@@ -115,7 +115,7 @@ function getOverviewSidebar(root: string, docsDir: string, prefix: string) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
function getBusinessProcessSidebarItems(root: string, prefix: string) {
|
|
118
|
-
const processDir = path.join(root, prefix.replace(/^\//, ''), '03-
|
|
118
|
+
const processDir = path.join(root, prefix.replace(/^\//, ''), '03-user-flows')
|
|
119
119
|
if (!fs.existsSync(processDir)) return []
|
|
120
120
|
try {
|
|
121
121
|
const entries = fs.readdirSync(processDir, { withFileTypes: true })
|
|
@@ -129,7 +129,7 @@ function getBusinessProcessSidebarItems(root: string, prefix: string) {
|
|
|
129
129
|
title = markdownNavText(entry.name, content) || nameWithoutExt
|
|
130
130
|
items.push({
|
|
131
131
|
text: title,
|
|
132
|
-
link: `${prefix}/03-
|
|
132
|
+
link: `${prefix}/03-user-flows/${nameWithoutExt}`
|
|
133
133
|
})
|
|
134
134
|
}
|
|
135
135
|
}
|
|
@@ -399,10 +399,10 @@ export default () => {
|
|
|
399
399
|
{ text: '01 Introduction', link: `${archPrefix}/01-introduction/` },
|
|
400
400
|
{ text: '02 Constraints', link: `${archPrefix}/02-constraints/` },
|
|
401
401
|
{
|
|
402
|
-
text: '03
|
|
402
|
+
text: '03 Luồng người dùng',
|
|
403
403
|
collapsed: true,
|
|
404
404
|
items: [
|
|
405
|
-
{ text: 'Catalog', link: `${archPrefix}/03-
|
|
405
|
+
{ text: 'Catalog', link: `${archPrefix}/03-user-flows/` },
|
|
406
406
|
...getBusinessProcessSidebarItems(projectRoot, archPrefix),
|
|
407
407
|
],
|
|
408
408
|
},
|
|
@@ -546,12 +546,12 @@ function auditBundleContent(rawText, filePath, pageType) {
|
|
|
546
546
|
// Quality warnings (non-blocking — does not affect totalGaps for structure)
|
|
547
547
|
// =========================================================================
|
|
548
548
|
|
|
549
|
-
if (!has(rawText, '
|
|
549
|
+
if (!has(rawText, 'scopeIn:')) {
|
|
550
550
|
addWarning(
|
|
551
|
-
'
|
|
552
|
-
'
|
|
553
|
-
'
|
|
554
|
-
'Declare
|
|
551
|
+
'WARN_NO_SCOPE_IN',
|
|
552
|
+
'scopeIn',
|
|
553
|
+
'scopeIn not declared — generated spec.md missing “Trong phạm vi” (PRD in-scope).',
|
|
554
|
+
'Declare scopeIn: | with bullets for what this screen/feature includes.'
|
|
555
555
|
);
|
|
556
556
|
}
|
|
557
557
|
|
|
@@ -559,8 +559,39 @@ function auditBundleContent(rawText, filePath, pageType) {
|
|
|
559
559
|
addWarning(
|
|
560
560
|
'WARN_NO_NON_GOALS',
|
|
561
561
|
'nonGoals',
|
|
562
|
-
'nonGoals not declared —
|
|
563
|
-
'Declare nonGoals: | with explicit
|
|
562
|
+
'nonGoals not declared — generated spec.md missing “Ngoài phạm vi” (PRD out-of-scope).',
|
|
563
|
+
'Declare nonGoals: | with explicit out-of-scope bullets.'
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (!has(rawText, 'nfr:')) {
|
|
568
|
+
addWarning(
|
|
569
|
+
'WARN_NO_NFR',
|
|
570
|
+
'nfr',
|
|
571
|
+
'nfr not declared — generated spec.md missing non-functional section (perf/security).',
|
|
572
|
+
'Declare nfr: | or link architecture/08-cross-cutting in prose.'
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const multiScreen =
|
|
577
|
+
has(rawText, 'nextScreenOnSuccess') ||
|
|
578
|
+
has(rawText, 'contextualAction') ||
|
|
579
|
+
has(rawText, 'sourceScreen:');
|
|
580
|
+
if (multiScreen && !has(rawText, 'userFlows:')) {
|
|
581
|
+
addWarning(
|
|
582
|
+
'WARN_NO_USER_FLOWS',
|
|
583
|
+
'userFlows',
|
|
584
|
+
'Màn có handoff / multi-screen nhưng chưa khai báo luồng người dùng (userFlows).',
|
|
585
|
+
'Declare userFlows: | — link FLOW-* hoặc mô tả journey ngắn.'
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (has(rawText, 'risks:')) {
|
|
590
|
+
addWarning(
|
|
591
|
+
'WARN_BUNDLE_RISKS_FORBIDDEN',
|
|
592
|
+
'risks',
|
|
593
|
+
'Rủi ro không ghi trên feature bundle — SSOT chỉ `architecture/11-risks/risk-register.md`.',
|
|
594
|
+
'Xóa key `risks:` khỏi bundle; thêm dòng vào risk-register.md (`RISK-*`). Dùng `/risk-register`.'
|
|
564
595
|
);
|
|
565
596
|
}
|
|
566
597
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* audit-flow-gaps.mjs
|
|
5
|
-
* Zero-dependency static audit script for FlowGrid
|
|
6
|
-
* Ensures 100% adherence to the 6-section
|
|
5
|
+
* Zero-dependency static audit script for FlowGrid User flow markdown files (FLOW-*.md).
|
|
6
|
+
* Ensures 100% adherence to the 6-section user flow standard and background logic rules.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import fs from 'fs';
|
|
@@ -2,11 +2,20 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { parse } from 'yaml'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function qaDirHasFiles(qaDir) {
|
|
6
|
+
if (!existsSync(qaDir)) return false
|
|
7
|
+
for (const name of readdirSync(qaDir)) {
|
|
8
|
+
if (/\.ya?ml$/i.test(name)) return true
|
|
9
|
+
if (name === 'open' && existsSync(path.join(qaDir, 'open'))) return true
|
|
10
|
+
}
|
|
11
|
+
return existsSync(path.join(qaDir, 'index.md'))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Walk up from a bundle dir to the docs hub (`qa/` or `architecture/`). */
|
|
6
15
|
export function findDocsHubRoot(fromDir) {
|
|
7
16
|
let dir = path.resolve(fromDir)
|
|
8
17
|
for (let i = 0; i < 40; i++) {
|
|
9
|
-
if (existsSync(path.join(dir, '
|
|
18
|
+
if (existsSync(path.join(dir, 'architecture')) || qaDirHasFiles(path.join(dir, 'qa'))) {
|
|
10
19
|
return dir
|
|
11
20
|
}
|
|
12
21
|
const parent = path.dirname(dir)
|
|
@@ -16,32 +25,73 @@ export function findDocsHubRoot(fromDir) {
|
|
|
16
25
|
return null
|
|
17
26
|
}
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
function listQaYamlPaths(hubRoot) {
|
|
29
|
+
const qaDir = path.join(hubRoot, 'qa')
|
|
30
|
+
const paths = []
|
|
31
|
+
if (!existsSync(qaDir)) return paths
|
|
32
|
+
for (const name of readdirSync(qaDir)) {
|
|
33
|
+
if (/\.ya?ml$/i.test(name)) paths.push(path.join(qaDir, name))
|
|
34
|
+
}
|
|
35
|
+
const openDir = path.join(qaDir, 'open')
|
|
36
|
+
if (existsSync(openDir)) {
|
|
37
|
+
for (const name of readdirSync(openDir)) {
|
|
38
|
+
if (/\.ya?ml$/i.test(name)) paths.push(path.join(openDir, name))
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return paths
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function qaAppliesToBundle(doc, stem, bundleId, bundlePath) {
|
|
45
|
+
if (stem.startsWith(`QA-${bundleId}-`)) return true
|
|
46
|
+
if (doc?.screen && String(doc.screen) === bundleId) return true
|
|
47
|
+
const tp = doc?.target?.path
|
|
48
|
+
if (tp && bundlePath) {
|
|
49
|
+
const base = path.basename(bundlePath)
|
|
50
|
+
if (String(tp).includes(base)) return true
|
|
51
|
+
}
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isOpenQa(doc) {
|
|
56
|
+
if (doc?.status === 'closed') return false
|
|
57
|
+
if (doc?.status === 'open') return true
|
|
58
|
+
if (doc?.resolution) return false
|
|
59
|
+
return true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function questionLine(doc) {
|
|
63
|
+
if (typeof doc?.question === 'string' && doc.question.trim()) {
|
|
64
|
+
return doc.question.trim().split('\n')[0]
|
|
65
|
+
}
|
|
66
|
+
const updates = Array.isArray(doc?.updates) ? doc.updates : []
|
|
67
|
+
const q = updates.find((u) => u?.kind === 'question')
|
|
68
|
+
if (q?.text) return String(q.text).trim().split('\n')[0]
|
|
69
|
+
const last = updates[updates.length - 1]
|
|
70
|
+
if (last?.text) return String(last.text).trim().split('\n')[0]
|
|
71
|
+
return ''
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function openQaCsv(hubRoot, bundleId, bundlePath = '') {
|
|
75
|
+
return getOpenQaDetails(hubRoot, bundleId, bundlePath).csv
|
|
21
76
|
}
|
|
22
77
|
|
|
23
|
-
export function getOpenQaDetails(hubRoot, bundleId) {
|
|
78
|
+
export function getOpenQaDetails(hubRoot, bundleId, bundlePath = '') {
|
|
24
79
|
if (!hubRoot || !bundleId) return { csv: '', techDebt: [] }
|
|
25
|
-
const openDir = path.join(hubRoot, 'qa', 'open')
|
|
26
|
-
if (!existsSync(openDir)) return { csv: '', techDebt: [] }
|
|
27
|
-
const prefix = `QA-${bundleId}-`
|
|
28
80
|
const ids = []
|
|
29
81
|
const techDebt = []
|
|
30
|
-
for (const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (!stem.startsWith(prefix)) continue
|
|
34
|
-
let id = stem
|
|
35
|
-
let kind = ''
|
|
36
|
-
let question = ''
|
|
82
|
+
for (const full of listQaYamlPaths(hubRoot)) {
|
|
83
|
+
const stem = path.basename(full).replace(/\.ya?ml$/i, '')
|
|
84
|
+
let doc = {}
|
|
37
85
|
try {
|
|
38
|
-
|
|
39
|
-
if (parsed && typeof parsed.id === 'string' && parsed.id.trim()) id = parsed.id.trim()
|
|
40
|
-
if (parsed && typeof parsed.kind === 'string') kind = parsed.kind
|
|
41
|
-
if (parsed && typeof parsed.question === 'string') question = parsed.question.trim().split('\n')[0]
|
|
86
|
+
doc = parse(readFileSync(full, 'utf8')) ?? {}
|
|
42
87
|
} catch {
|
|
43
|
-
|
|
88
|
+
continue
|
|
44
89
|
}
|
|
90
|
+
if (!isOpenQa(doc)) continue
|
|
91
|
+
if (!qaAppliesToBundle(doc, stem, bundleId, bundlePath)) continue
|
|
92
|
+
const id = typeof doc.id === 'string' && doc.id.trim() ? doc.id.trim() : stem
|
|
93
|
+
const kind = typeof doc.kind === 'string' ? doc.kind : ''
|
|
94
|
+
const question = questionLine(doc)
|
|
45
95
|
ids.push(id)
|
|
46
96
|
if (kind === 'tech-debt') {
|
|
47
97
|
techDebt.push({ id, status: 'open', summary: question })
|
|
@@ -57,7 +107,7 @@ const QA_FIELD = 'Q&A'
|
|
|
57
107
|
/** Set or remove `Q&A` on a business spec object (comma-separated ids). */
|
|
58
108
|
export function applyOpenQaField(spec, bundlePath, bundleId) {
|
|
59
109
|
const hub = findDocsHubRoot(path.dirname(path.resolve(bundlePath)))
|
|
60
|
-
const qa = getOpenQaDetails(hub, bundleId)
|
|
110
|
+
const qa = getOpenQaDetails(hub, bundleId, bundlePath)
|
|
61
111
|
if (qa.csv) spec[QA_FIELD] = qa.csv
|
|
62
112
|
else delete spec[QA_FIELD]
|
|
63
113
|
if (qa.techDebt && qa.techDebt.length > 0) spec.pendingTechDebt = qa.techDebt
|
|
@@ -138,7 +138,7 @@ function absUnder(root, rel) {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
function findFlowMarkdown(docsRoot, id) {
|
|
141
|
-
const catalog = path.join(docsRoot, 'architecture', '03-
|
|
141
|
+
const catalog = path.join(docsRoot, 'architecture', '03-user-flows', `${id}.md`)
|
|
142
142
|
if (existsSync(catalog)) return catalog
|
|
143
143
|
const hits = []
|
|
144
144
|
const walk = (dir) => {
|
|
@@ -150,7 +150,7 @@ function findFlowMarkdown(docsRoot, id) {
|
|
|
150
150
|
walk(p)
|
|
151
151
|
continue
|
|
152
152
|
}
|
|
153
|
-
if (ent.name === `${id}.md` && p.split(path.sep).join('/').includes('/common/
|
|
153
|
+
if (ent.name === `${id}.md` && p.split(path.sep).join('/').includes('/common/user-flows/')) {
|
|
154
154
|
hits.push(p)
|
|
155
155
|
}
|
|
156
156
|
}
|
|
@@ -206,7 +206,7 @@ export function resolveHubId(repoRoot, id, mode = 'testcase') {
|
|
|
206
206
|
const docsRoot = getDocsRoot()
|
|
207
207
|
const md = findFlowMarkdown(docsRoot, id)
|
|
208
208
|
if (!md) {
|
|
209
|
-
throw new Error(`Unknown FLOW ${id} — expected architecture/03-
|
|
209
|
+
throw new Error(`Unknown FLOW ${id} — expected architecture/03-user-flows/${id}.md or …/common/user-flows/${id}.md`)
|
|
210
210
|
}
|
|
211
211
|
notes.push(`flow markdown: ${path.relative(repoRoot, md)}`)
|
|
212
212
|
return { kind: 'flow', id, paths: [md], notes }
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
|
|
7
7
|
# /legacy — Legacy Context Modifier
|
|
8
8
|
|
|
9
|
-
**Skill Modifier:** When a member uses `/legacy` alongside another specialized skill (e.g., `/legacy /spec`, `/legacy /overview`, `/legacy /
|
|
9
|
+
**Skill Modifier:** When a member uses `/legacy` alongside another specialized skill (e.g., `/legacy /spec`, `/legacy /overview`, `/legacy /user-flow`), the Agent MUST enforce the following behavioral shifts.
|
|
10
10
|
|
|
11
11
|
## Behavioral Shifts (Context Shift)
|
|
12
12
|
|
|
@@ -18,7 +18,7 @@ disable-model-invocation: true
|
|
|
18
18
|
- **Tier 1 (Page / API Detail — `/legacy /spec`)**:
|
|
19
19
|
- ONLY audit internal page/API scope: Check **missing Validation** (field validation rules, max length, format regex...) and **Local Security** (e.g. Laravel `@csrf`, Auth guard/middleware on route, input sanitization).
|
|
20
20
|
- Do NOT perform cross-system end-to-end vulnerability audits at this level.
|
|
21
|
-
- **Tier 2 (Cross-Flow /
|
|
21
|
+
- **Tier 2 (Cross-Flow / User flow — `/legacy /user-flow`, Module, Surface)**:
|
|
22
22
|
- Audit **End-to-End Business Flow Gaps**: Check data/status misalignment between Step 1 (Screen 1) and Step 2 (Screen 2), orphan APIs/steps, broken flow steps, and missing rollback/confirmation handling on failure.
|
|
23
23
|
|
|
24
24
|
3. **Metadata Updates (When Applicable):**
|
|
@@ -17,7 +17,7 @@ surfaces/<surface>/CMP-*/<slug>/ # no modules/ segment
|
|
|
17
17
|
|
|
18
18
|
1. **PRE-FLIGHT:** First action = `{{FLOWGRID_READ_TOOL}}` / read target `SKILL.md`. Never memory.
|
|
19
19
|
4. **NO RAM CACHING:** Durable results → disk immediately. Prior file = next input.
|
|
20
|
-
5. **ZERO BUSINESS HALLUCINATION:** Data only from User prompt or ArtifactGraph. Gaps → AskQuestion (MUST include "Log as Tech Debt" option); if member selects "Log as Tech Debt" → `qa
|
|
20
|
+
5. **ZERO BUSINESS HALLUCINATION:** Data only from User prompt or ArtifactGraph. Gaps → AskQuestion (MUST include "Log as Tech Debt" option); if member selects "Log as Tech Debt" → `qa/<SHORT>_NNNN.yaml` + `#missing_info QA-…`. No invented business fields.
|
|
21
21
|
6. **GRILL HARD GATE:** AG re-check → micro-scope → propose → **STOP for Confirm** before product SSOT write.
|
|
22
22
|
7. **HUMAN DSL:** Common/DSL only via `/common`, `/docs-mark`, custom-base, or grill Confirm. `/spec` consumes only (no `common/yaml`).
|
|
23
23
|
|
|
@@ -26,6 +26,6 @@ surfaces/<surface>/CMP-*/<slug>/ # no modules/ segment
|
|
|
26
26
|
```text
|
|
27
27
|
{{FLOWGRID_READ_TOOL}} SKILL.md
|
|
28
28
|
→ durable writes immediately (No RAM)
|
|
29
|
-
→ AskQuestion or qa
|
|
29
|
+
→ AskQuestion or qa + grill Confirm before SSOT fill
|
|
30
30
|
→ common/DSL only when human-gated
|
|
31
31
|
```
|
|
@@ -29,6 +29,6 @@ flowgrid render
|
|
|
29
29
|
|
|
30
30
|
## Still missing after grill?
|
|
31
31
|
|
|
32
|
-
`#tech-debt:QA-*` + `qa
|
|
32
|
+
`#tech-debt:QA-*` + `qa/` — or `/qa-resolve`. Do not ship silent gaps.
|
|
33
33
|
|
|
34
34
|
Registry: BE checkout `registries/codegen.registry.json` (after `registry:sync`).
|
|
@@ -28,7 +28,7 @@ Path: `…/common/yaml/<slug>/01-backend-spec.yaml` — same trio rules, scan be
|
|
|
28
28
|
|
|
29
29
|
## Missing facts
|
|
30
30
|
|
|
31
|
-
AskQuestion (Recommended / Other / Tech Debt) → `qa
|
|
31
|
+
AskQuestion (Recommended / Other / Tech Debt) → `qa/*` — **no** `openQuestions` in YAML.
|
|
32
32
|
|
|
33
33
|
## Field rename guard
|
|
34
34
|
|
|
@@ -10,7 +10,7 @@ Hashtag only (via `/api-spec`, `/grill-api-spec`, `/api-update`) — **not** a s
|
|
|
10
10
|
## Rules
|
|
11
11
|
|
|
12
12
|
- Never edit `ir/design.yaml` for external contract detail.
|
|
13
|
-
- No invented secrets — AskQuestion or `qa
|
|
13
|
+
- No invented secrets — AskQuestion or `qa/`.
|
|
14
14
|
- Grill: timeout/retry/idempotency must be explicit before `api-gen`.
|
|
15
15
|
|
|
16
16
|
Hub: `docs/references/skills/call-external.md`.
|
|
@@ -25,7 +25,7 @@ Inside every `common/`:
|
|
|
25
25
|
```text
|
|
26
26
|
common/
|
|
27
27
|
patterns/ ← /common Markdown (BA/QA rules)
|
|
28
|
-
|
|
28
|
+
user-flows/ ← module/cluster FLOW-*.md (Luồng người dùng; not architecture catalog)
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
Optional: `data-model/`, `integrations/`, `db-erd.md`, `cross-service.md` at the same LCA (see skills `/db-erd`, `/cross-service`).
|
|
@@ -38,20 +38,20 @@ If the same pattern already exists at a **narrower** `common/`, **reuse it** (re
|
|
|
38
38
|
|
|
39
39
|
## 3. Consume order (`/spec`, `/grill-*`, `/api-spec`)
|
|
40
40
|
|
|
41
|
-
From the function folder, walk **up** and read the **nearest** `common/patterns/*.md` (and FLOW under `
|
|
41
|
+
From the function folder, walk **up** and read the **nearest** `common/patterns/*.md` (and FLOW under `user-flows/` when relevant). Do not author duplicate YAML CMN bundles.
|
|
42
42
|
|
|
43
43
|
Same walk for `patterns/` Markdown.
|
|
44
44
|
|
|
45
|
-
## 4.
|
|
45
|
+
## 4. Luồng người dùng (`FLOW-*`)
|
|
46
46
|
|
|
47
47
|
| Scope of the flow | File |
|
|
48
48
|
|-------------------|------|
|
|
49
|
-
| Org / cross-surface / “hero” catalog | `architecture/03-
|
|
50
|
-
| Many modules, one surface | `surfaces/<surface>/common/
|
|
51
|
-
| Whole module, several clusters | `surfaces/<surface>/<CMP-id>/common/
|
|
52
|
-
| One cluster / submodule only | `surfaces/<surface>/<CMP-id>/<NN>/common/
|
|
49
|
+
| Org / cross-surface / “hero” catalog | `architecture/03-user-flows/FLOW-*.md` (MCP `flowgrid_docs_user_flows`) |
|
|
50
|
+
| Many modules, one surface | `surfaces/<surface>/common/user-flows/FLOW-*.md` |
|
|
51
|
+
| Whole module, several clusters | `surfaces/<surface>/<CMP-id>/common/user-flows/FLOW-*.md` |
|
|
52
|
+
| One cluster / submodule only | `surfaces/<surface>/<CMP-id>/<NN>/common/user-flows/FLOW-*.md` |
|
|
53
53
|
|
|
54
|
-
Do **not** drop `FLOW-*.md` beside a single `W-*` bundle. Do **not** write module-internal flows only under `architecture/03-
|
|
54
|
+
Do **not** drop `FLOW-*.md` beside a single `W-*` bundle. Do **not** write module-internal flows only under `architecture/03-user-flows/` (optional **link** from the catalog to the product path).
|
|
55
55
|
|
|
56
56
|
`/db-erd` and `/cross-service` use the **same** `common/` LCA (`common/db-erd.md`, `common/cross-service.md`), not a second invented folder name.
|
|
57
57
|
|
|
@@ -4,12 +4,12 @@ Hub SSOT: `docs/workflows/design-leaf-signoff.md` — **không** engine gate.
|
|
|
4
4
|
|
|
5
5
|
## Rubric 6 mục (lead/BA)
|
|
6
6
|
|
|
7
|
-
1. `flowgrid audit spec <bundle> --type <profile>` — `gaps[]` xử lý; `confirms[]` chốt hoặc `qa
|
|
7
|
+
1. `flowgrid audit spec <bundle> --type <profile>` — `gaps[]` xử lý; `confirms[]` chốt hoặc `qa`.
|
|
8
8
|
2. VitePress `ir/generated/spec.md` (+ `data-model.md`) — stories, list columns, AC không placeholder.
|
|
9
9
|
3. Spot 2–3 scenario ↔ `design.actions` / validation messages VI.
|
|
10
10
|
4. `api/.../01-backend-spec.yaml` ↔ `ir/design.yaml` apiRef; `audit fe-be` nếu có BE.
|
|
11
11
|
5. `grillStatus.dev: done` (theo flow); `gen:dry` pass; `bundle.gen` đầy đủ.
|
|
12
|
-
6. `qa
|
|
12
|
+
6. `qa` rỗng hoặc debt đã accept.
|
|
13
13
|
|
|
14
14
|
Optional: **Data model** — `db-erd` LCA + `design.sections[].db` / `spec.entities` (xem hub `architecture-data.md`). `/grill-prototype` SSOT issues; `registry:sync` trên FE standard base.
|
|
15
15
|
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"docs-hub": [
|
|
16
16
|
".cursor/extracts/docs-phase-hooks.md",
|
|
17
17
|
".cursor/extracts/agent-execution-protocol.md",
|
|
18
|
-
".cursor/extracts/qa-inbox.md"
|
|
18
|
+
".cursor/extracts/qa-inbox.md",
|
|
19
|
+
".cursor/extracts/qa-team.md"
|
|
19
20
|
],
|
|
20
21
|
"spec-requirement": [
|
|
21
22
|
".cursor/extracts/spec-requirement.md",
|
|
@@ -7,28 +7,37 @@ There is **no** `openQuestions` on YAML. Gaps use **AskQuestion** in the skill s
|
|
|
7
7
|
AskQuestion: MUST include your Recommended options AND an explicit "Log as Tech Debt (Pending)" option. (The UI automatically provides the 3rd "Other" option). **STOP**.
|
|
8
8
|
|
|
9
9
|
- Member picks A/B/C (Recommended) → write that fact into the real spec field (`design`, `01`, …).
|
|
10
|
-
- Member picks **Log as Tech Debt (Pending)** → create
|
|
10
|
+
- Member picks **Log as Tech Debt (Pending)** → create **one** `qa/<SHORT>_NNNN.yaml` with first `updates[]` line `kind: question`. Do not invent the answer.
|
|
11
11
|
- Member picks **Other** and types a decision → write that text into the spec field.
|
|
12
12
|
|
|
13
13
|
## When to create a file
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
**[MANDATORY]** Read whole `.flowgrid/templates/qa-item.yaml` + `.flowgrid/templates/qa-authoring.md` before writing.
|
|
16
|
+
|
|
17
|
+
1. Pick **SHORT** slug (stable) — see `suggestQaShort(W-*)` in authoring doc.
|
|
18
|
+
2. Next id: glob `qa/<SHORT>_*.yaml` **or** `nextQaId(hub, SHORT)` (`engines/docs/lib/qa-item.mjs`).
|
|
19
|
+
3. Copy golden template → `qa/<SHORT>_NNNN.yaml`:
|
|
20
|
+
- `schema: flowgrid-qa-item/v1`
|
|
21
|
+
- `id` = basename; `status: open`; `target.path` + `target.at`; `screen` / `bundleId` / `skill` / `kind`
|
|
22
|
+
4. First `updates[]`: `at` = `YYYYMMDD HH:mm` (`formatQaAt`), `kind: question`, `text` = open question.
|
|
23
|
+
5. Point spec: `#missing_info <id>` / `#tech-debt:<id>` on `target.at`.
|
|
24
|
+
6. `flowgrid split` — `ir/spec.yaml` Q&A lists open ids.
|
|
25
|
+
|
|
26
|
+
Optional: `validateQaItem` / schema `templates/schemas/qa-item.schema.json`.
|
|
20
27
|
|
|
21
28
|
## Close (`/qa-resolve`)
|
|
22
29
|
|
|
23
|
-
Prompt: `/qa-resolve
|
|
30
|
+
Prompt: `/qa-resolve <id>` plus **solution** on following lines.
|
|
31
|
+
|
|
32
|
+
Append `kind: answer` to **same file**; patch bundle/01; `status: closed`; gỡ tags; `flowgrid split`.
|
|
24
33
|
|
|
25
|
-
|
|
34
|
+
## Review (team)
|
|
26
35
|
|
|
27
|
-
|
|
36
|
+
`/qa-review <id>` → append `kind: review` to **same file** — see `qa-team.md`.
|
|
28
37
|
|
|
29
38
|
## Do not block grill
|
|
30
39
|
|
|
31
|
-
`grillStatus` may be `done` while `
|
|
40
|
+
`grillStatus` may be `done` while `status: open` QA files exist.
|
|
32
41
|
|
|
33
42
|
## Not this folder
|
|
34
43
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# QA — one file, append timeline (docs hub)
|
|
2
|
+
|
|
3
|
+
Hub: `docs/workflows/qa-team.md`.
|
|
4
|
+
|
|
5
|
+
## File naming
|
|
6
|
+
|
|
7
|
+
`qa/<SHORT>_<NNNN>.yaml` — e.g. `HOTEL-LIST_0001.yaml`. Next index: glob same prefix, max NNNN + 1.
|
|
8
|
+
|
|
9
|
+
`id` inside file = basename without `.yaml`.
|
|
10
|
+
|
|
11
|
+
## Rules
|
|
12
|
+
|
|
13
|
+
1. **One case = one file** for life of that question thread.
|
|
14
|
+
2. **`updates[]` append-only** — `at` as `YYYYMMDD HH:mm`, `kind`: question | answer | review | note.
|
|
15
|
+
3. **`qa/index.md`** is render-only catalog — do not put business log in index.
|
|
16
|
+
4. **needs-change:** append `kind: review` with text; set `status: open`; do **not** create a second file.
|
|
17
|
+
|
|
18
|
+
## Open (Tech Debt)
|
|
19
|
+
|
|
20
|
+
Template: `.flowgrid/templates/qa-item.yaml` · rules: `qa-authoring.md` · `schema: flowgrid-qa-item/v1`.
|
|
21
|
+
|
|
22
|
+
Create file + first update `kind: question`. Tag field: `#missing_info <id>` / `#tech-debt:<id>`.
|
|
23
|
+
|
|
24
|
+
## /qa-resolve
|
|
25
|
+
|
|
26
|
+
Append `kind: answer` with user solution text; patch `target.path` / `target.at`; remove QA tags from spec; `status: closed`; `flowgrid split`.
|
|
27
|
+
|
|
28
|
+
## /qa-review
|
|
29
|
+
|
|
30
|
+
Append `kind: review` only. If needs-change, `status: open`.
|
|
31
|
+
|
|
32
|
+
Legacy `qa/` paths: migrate to flat `qa/<id>.yaml` when touching old hubs.
|
|
@@ -14,7 +14,7 @@ Set `breaking: true`; require re-grill (`/grill-api-spec` or `/grill-dev`) befor
|
|
|
14
14
|
|
|
15
15
|
## Deferred work
|
|
16
16
|
|
|
17
|
-
Use `pendingTechDebt[]` with `id: QA-<feature.id>-NNNN` + matching `qa
|
|
17
|
+
Use `pendingTechDebt[]` with `id: QA-<feature.id>-NNNN` + matching `qa/` file — not prose in `requirements`.
|
|
18
18
|
|
|
19
19
|
## OpenAPI
|
|
20
20
|
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
# Leaf bundle ↔ PRD lite (map for /spec)
|
|
2
2
|
|
|
3
|
-
| PRD section | Bundle /
|
|
3
|
+
| PRD section | Bundle key → `ir/generated/spec.md` |
|
|
4
4
|
| --- | --- |
|
|
5
|
-
|
|
|
6
|
-
|
|
|
7
|
-
|
|
|
8
|
-
|
|
|
9
|
-
| User
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
| Open questions | `qa
|
|
5
|
+
| Goals / background | `summary` |
|
|
6
|
+
| In scope | `scopeIn` |
|
|
7
|
+
| Out of scope | `nonGoals` |
|
|
8
|
+
| Features & requirements | `userStories` + `design.*` (render sections) |
|
|
9
|
+
| User flows | `userFlows` (+ FLOW-* docs) |
|
|
10
|
+
| Data & API | `entities` + links `data-model.md` / `api.md` / `01` |
|
|
11
|
+
| NFR | `nfr` |
|
|
12
|
+
| Risks | **Chỉ** `architecture/11-risks/risk-register.md` — không key trên bundle |
|
|
13
|
+
| Open questions | `qa/*.yaml` → `Q&A` + Phụ lục |
|
|
14
14
|
|
|
15
|
-
**
|
|
16
|
-
|
|
17
|
-
**BA deliverable:** `ir/generated/spec.md` (TOC, overview, metrics, non-goals) after `flowgrid split` + `flowgrid render`.
|
|
15
|
+
**BA deliverable:** `flowgrid split` + `flowgrid render` → `ir/generated/spec.md` (TOC PRD). Audit: `WARN_NO_SCOPE_IN`, `WARN_NO_NON_GOALS`, `WARN_NO_NFR`, `WARN_NO_USER_FLOWS` (when handoff). Rủi ro: `/risk-register` + `audit risks` (không `risks:` trên bundle).
|
|
18
16
|
|
|
19
17
|
**Profile trim:** list → giữ Initial Load, Affordances (nếu có), Exceptions; bỏ scenario form nếu không có `ui.form`. create/detail tương tự — không copy 6 scenario khi không áp dụng.
|
|
@@ -1,45 +1,14 @@
|
|
|
1
|
-
# Module
|
|
1
|
+
# Module index template
|
|
2
2
|
|
|
3
|
-
Path: `surfaces/<surface>/CMP-{NN}-{slug}/index.md` — **MD only
|
|
3
|
+
Path: `surfaces/<surface>/CMP-{NN}-{slug}/index.md` — **MD only**.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
# CMP-{NN} — Name
|
|
5
|
+
Copy from: `templates/project-skeleton/surfaces/_module-index.template.md`
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
Required sections (English keys — `flowgrid audit hub-prd`):
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
- `## Goals` {#goals}
|
|
10
|
+
- `## Scope` {#scope} — In scope / Out of scope
|
|
11
|
+
- `## Features overview` {#features-overview}
|
|
12
|
+
- `## Depends on` {#depends-on}
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- [Chức năng / CMP khác giữ SSOT — không copy bundle vào module này]
|
|
15
|
-
- [Hạ tầng / deployment — `architecture/`]
|
|
16
|
-
|
|
17
|
-
## Depends on
|
|
18
|
-
|
|
19
|
-
| ID / artifact | Lý do |
|
|
20
|
-
| --- | --- |
|
|
21
|
-
| `CMP-…` / `FLOW-…` | [Upstream data hoặc quy trình] |
|
|
22
|
-
| `API-…` | [Contract reuse — link `01-backend-spec`] |
|
|
23
|
-
|
|
24
|
-
| | |
|
|
25
|
-
|--|--|
|
|
26
|
-
| **ID** | `CMP-{NN}` |
|
|
27
|
-
| **Business Process** | Module/cluster: `…/common/processes/FLOW-…`. Catalog: [`architecture/03-business-process/`](/architecture/03-business-process/) |
|
|
28
|
-
| **Functions** | `<function-slug>`, … |
|
|
29
|
-
| **Screens** | `W-…` |
|
|
30
|
-
| **APIs** | `API-…` |
|
|
31
|
-
|
|
32
|
-
\`\`\`mermaid
|
|
33
|
-
flowchart LR
|
|
34
|
-
CMP[CMP-{NN}]
|
|
35
|
-
W[W-…]
|
|
36
|
-
API[API-…]
|
|
37
|
-
CMP --> W
|
|
38
|
-
CMP --> API
|
|
39
|
-
\`\`\`
|
|
40
|
-
|
|
41
|
-
## Code paths
|
|
42
|
-
|
|
43
|
-
- [`<function-slug>/code/W-…/`](./<function-slug>/code/W-…/)
|
|
44
|
-
- [`<function-slug>/code/API-…/`](./<function-slug>/code/API-…/)
|
|
45
|
-
```
|
|
14
|
+
Prose: `docs-hub.locale.yaml` → `contentLocale`. No Personas / Success metrics tables on hub.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Overview PRD headings (English keys — audit hub-prd)
|
|
2
|
+
|
|
3
|
+
Required on `overview/index.md`:
|
|
4
|
+
|
|
5
|
+
- `## Goals` {#goals}
|
|
6
|
+
- `## Background` {#background}
|
|
7
|
+
- `## Scope` {#scope} with in-scope / out-of-scope bullets
|
|
8
|
+
|
|
9
|
+
Prose language: `docs-hub.locale.yaml` → `contentLocale` (BCP-47 tag, e.g. `vi`, `en`, `ja`).
|
|
10
|
+
|
|
11
|
+
Forbidden hub sections: Personas tables, Success metrics (manage outside hub).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Risk register — format (SSOT duy nhất)
|
|
2
|
+
|
|
3
|
+
**File:** `architecture/11-risks/risk-register.md`
|
|
4
|
+
**Không** có `risks:` trên `*.bundle.yaml` — audit spec báo `WARN_BUNDLE_RISKS_FORBIDDEN` nếu còn key đó.
|
|
5
|
+
|
|
6
|
+
## Table: one row = one risk
|
|
7
|
+
|
|
8
|
+
| ID | Type | Description | Limit | Need | Gap | Impact | Mitigation | Status |
|
|
9
|
+
|
|
10
|
+
- **ID:** `RISK-<TOPIC>-<NNN>`
|
|
11
|
+
- **Limit / Need:** counts + units (e.g. `500 emails/day` vs `~700 emails/day` peak)
|
|
12
|
+
- **Gap:** `+200 (~40%)` or equivalent
|
|
13
|
+
- **Status:** `Open` | `Watching` | `Closed` | `Accepted` (prose may use hub `contentLocale`)
|
|
14
|
+
|
|
15
|
+
Ví dụ đầy đủ: xem dòng `RISK-QUOTA-EMAIL-001` trong skeleton `risk-register.md`.
|
|
16
|
+
|
|
17
|
+
## Quick stats (top of file)
|
|
18
|
+
|
|
19
|
+
Member cập nhật tay (tổng mở, có chênh quota, đã có phương án).
|
|
20
|
+
|
|
21
|
+
## VitePress
|
|
22
|
+
|
|
23
|
+
Trang MD trong Architecture → 11 Risks → **risk-register** (không render từ feature).
|
|
24
|
+
|
|
25
|
+
## Audit & skill
|
|
26
|
+
|
|
27
|
+
- `flowgrid audit risks architecture/11-risks/risk-register.md`
|
|
28
|
+
- `/risk-register`
|