@shanyucoder/flowgrid 0.1.5 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +1 -1
  2. package/adapters/laravel/registries/codegen.registry.json +9 -9
  3. package/bin/flowgrid.mjs +326 -115
  4. package/bin/lib/agent-mcp.mjs +4 -0
  5. package/bin/lib/agent-profiles.mjs +30 -6
  6. package/bin/lib/audit-run.mjs +1 -1
  7. package/bin/lib/cli-update.mjs +48 -8
  8. package/bin/lib/doctor.mjs +90 -3
  9. package/bin/lib/harness-overlay.mjs +12 -5
  10. package/bin/lib/harness-sync.mjs +17 -3
  11. package/bin/lib/init-adapters.mjs +75 -0
  12. package/bin/lib/init-scaffold.mjs +9 -0
  13. package/bin/lib/inject-consumer-scripts.mjs +115 -0
  14. package/bin/lib/merge-stack-config.mjs +89 -0
  15. package/bin/lib/project-gitignore.mjs +1 -0
  16. package/bin/lib/repo-maps-align.mjs +203 -0
  17. package/dist/graph/config/load-config.js +7 -2
  18. package/dist/graph/config/load-config.js.map +1 -1
  19. package/dist/graph/mcp/tools.js +1 -1
  20. package/dist/graph/mcp/tools.js.map +1 -1
  21. package/dist/graph/registry/load-registries.d.ts +1 -0
  22. package/dist/graph/registry/load-registries.js +14 -1
  23. package/dist/graph/registry/load-registries.js.map +1 -1
  24. package/engines/cases/render-cases.mjs +67 -5
  25. package/engines/docs/lib/render-api-summary-markdown.mjs +28 -0
  26. package/engines/docs/lib/render-bundle-markdown.mjs +77 -2
  27. package/engines/docs/lib/render-data-model-markdown.mjs +171 -0
  28. package/engines/docs/lib/render-design-tables.mjs +89 -7
  29. package/engines/docs/lib/render-template.mjs +6 -0
  30. package/engines/docs/render-docs.mjs +6 -2
  31. package/engines/docs/vitepress/config.ts +7 -7
  32. package/engines/docs/vitepress/surfaces-nav.mjs +45 -14
  33. package/engines/openapi/check-backend-spec.mjs +2 -2
  34. package/engines/openapi/lib/markdown-table.mjs +8 -0
  35. package/engines/openapi/lib/render-backend-spec-markdown.mjs +78 -0
  36. package/engines/registry-sync/be-capabilities-sync.mjs +118 -0
  37. package/engines/registry-sync/fe-design-sync.mjs +258 -0
  38. package/engines/registry-sync/run-registry-sync.mjs +107 -0
  39. package/engines/shared/e2e-output-layout.mjs +68 -0
  40. package/engines/shared/flowgrid-e2e-root.mjs +19 -0
  41. package/engines/shared/resolve-flowgrid-context.mjs +176 -0
  42. package/engines/spec/lib/audit-api-gaps.mjs +1 -1
  43. package/engines/spec/lib/audit-bundle-gaps.mjs +92 -3
  44. package/engines/spec/lib/audit-db-tables.mjs +529 -0
  45. package/engines/spec/lib/audit-e2e-coverage.mjs +88 -30
  46. package/engines/spec/lib/bundle-schema.mjs +4 -1
  47. package/engines/spec/split-bundle.mjs +11 -1
  48. package/engines/testcase/runners/generate-api.mjs +23 -23
  49. package/engines/testcase/runners/generate.mjs +19 -17
  50. package/engines/testcase/runners/lib/bootstrap-context.mjs +44 -0
  51. package/engines/testcase/runners/lib/write-files.mjs +37 -9
  52. package/harness/agents/antigravity/rules/antigravity-mcp.mdc +12 -0
  53. package/harness/agents/gemini/rules/gemini-mcp.mdc +11 -0
  54. package/harness/agents/gemini_antigravity/rules/antigravity-mcp.mdc +8 -6
  55. package/harness/be/skills/{grill-api → audit-api}/SKILL.md +8 -7
  56. package/harness/common/extracts/artifact-graph.md +2 -2
  57. package/harness/common/extracts/artifactgraph-phase-hooks.md +2 -2
  58. package/harness/common/extracts/docs-mark-detect.md +2 -2
  59. package/harness/common/extracts/entity-relationship.md +23 -0
  60. package/harness/common/rules/flowgrid-ux-common.mdc +3 -3
  61. package/harness/common/skills/configure-repo-maps/SKILL.md +4 -2
  62. package/harness/docs/extracts/agent-execution-protocol.md +1 -1
  63. package/harness/docs/extracts/api-codegen-readiness.md +34 -0
  64. package/harness/docs/extracts/api-codegen-tags.md +30 -0
  65. package/harness/docs/extracts/api-contract.md +43 -0
  66. package/harness/docs/extracts/api-spec-sync.md +35 -0
  67. package/harness/docs/extracts/artifactgraph-hooks-docs.md +1 -1
  68. package/harness/docs/extracts/call-external.md +16 -0
  69. package/harness/docs/extracts/common-scope.md +9 -10
  70. package/harness/docs/extracts/db-audit-wizard.md +45 -0
  71. package/harness/docs/extracts/derived-data.md +18 -0
  72. package/harness/docs/extracts/design-leaf-signoff.md +16 -0
  73. package/harness/docs/extracts/extract-registry.docs.json +9 -1
  74. package/harness/docs/extracts/spec-core.md +7 -3
  75. package/harness/docs/extracts/spec-evolution.md +21 -0
  76. package/harness/docs/extracts/spec-prd-lite.md +19 -0
  77. package/harness/docs/extracts/spec-requirement.md +6 -2
  78. package/harness/docs/extracts/spec-ssot-prep.md +25 -0
  79. package/harness/docs/extracts/tpl-module.md +12 -0
  80. package/harness/docs/extracts/verify-gate.md +33 -0
  81. package/harness/docs/extracts/wire-spec-feedback.md +31 -0
  82. package/harness/docs/rules/agent-compliance.mdc +1 -1
  83. package/harness/docs/rules/team-flow-spec.mdc +3 -4
  84. package/harness/docs/skills/adopt/SKILL.md +2 -0
  85. package/harness/docs/skills/api/SKILL.md +4 -5
  86. package/harness/docs/skills/api-spec/SKILL.md +19 -6
  87. package/harness/docs/skills/api-update/SKILL.md +3 -3
  88. package/harness/docs/skills/architecture/SKILL.md +1 -1
  89. package/harness/docs/skills/business-process/SKILL.md +2 -0
  90. package/harness/docs/skills/common/SKILL.md +2 -2
  91. package/harness/docs/skills/common-spec/SKILL.md +10 -47
  92. package/harness/docs/skills/db-erd/SKILL.md +26 -0
  93. package/harness/docs/skills/grill/SKILL.md +28 -22
  94. package/harness/docs/skills/grill-api/SKILL.md +4 -6
  95. package/harness/docs/skills/grill-api-spec/SKILL.md +44 -24
  96. package/harness/docs/skills/grill-bqa/SKILL.md +28 -13
  97. package/harness/docs/skills/grill-common-spec/SKILL.md +10 -35
  98. package/harness/docs/skills/grill-dev/SKILL.md +7 -6
  99. package/harness/docs/skills/grill-docs/SKILL.md +10 -3
  100. package/harness/docs/skills/module/SKILL.md +3 -1
  101. package/harness/docs/skills/openapi/SKILL.md +2 -1
  102. package/harness/docs/skills/overview/SKILL.md +7 -1
  103. package/harness/docs/skills/spec/SKILL.md +42 -9
  104. package/harness/docs/skills/update-spec/SKILL.md +4 -1
  105. package/harness/fe/extracts/wire-audit-loop.md +72 -0
  106. package/harness/fe/extracts/wire-phase.md +45 -0
  107. package/harness/fe/rules/platform-design-vocabulary.mdc +1 -1
  108. package/harness/fe/rules/team-flow-prototype.mdc +8 -4
  109. package/harness/fe/skills/gen-common/SKILL.md +11 -84
  110. package/harness/fe/skills/grill-prototype/SKILL.md +51 -25
  111. package/harness/fe/skills/grill-test/SKILL.md +78 -20
  112. package/harness/fe/skills/grill-wire/SKILL.md +81 -0
  113. package/harness/fe/skills/prototype/SKILL.md +3 -2
  114. package/harness/fe/skills/wire/SKILL.md +7 -2
  115. package/harness/shared/AGENTS.md +3 -3
  116. package/harness/shared/SSOT_AGENT_PROTOCOL.md +3 -3
  117. package/harness/tests/extracts/grill-api-hook.md +69 -0
  118. package/harness/tests/extracts/grill-scenario-flow.md +39 -0
  119. package/harness/tests/extracts/grill-screen-tc.md +40 -0
  120. package/harness/tests/extracts/testcase-gen-cli.md +57 -0
  121. package/harness/tests/extracts/testcase-plan.md +29 -0
  122. package/harness/tests/extracts/tests-verify-gate.md +29 -0
  123. package/harness/tests/extracts/wire-test-handoff.md +37 -0
  124. package/harness/tests/skills/grill-testcase/SKILL.md +1 -0
  125. package/harness/tests/skills/test-api/SKILL.md +14 -6
  126. package/harness/tests/skills/testcase/SKILL.md +3 -1
  127. package/harness/tests/templates/TC.example-api.yaml +7 -1
  128. package/harness/tests/templates/TC.example.yaml +4 -1
  129. package/harness/tests/templates/tpl-testcase-plan.md +75 -0
  130. package/package.json +1 -1
  131. package/stacks/fastapi.json +1 -0
  132. package/stacks/laravel.json +1 -0
  133. package/stacks/nestjs.json +72 -0
  134. package/stacks/nextjs-nest.json +1 -0
  135. package/stacks/nuxt4-nest.json +1 -0
  136. package/templates/project-skeleton/architecture/03-business-process/FLOW-template.md +2 -0
  137. package/templates/project-skeleton/overview/index.md +68 -2
  138. package/templates/project-skeleton/overview/operational-areas/_template.md +37 -0
  139. package/templates/project-skeleton/surfaces/common/data-model/index.md +10 -2
  140. package/templates/shared/api-03-mock.stub.yaml +14 -0
  141. package/templates/shared/backend-api.bundle.yaml +3 -0
  142. package/templates/shared/backend-api.yaml +4 -0
  143. package/templates/shared/be-capabilities.registry.base.json +8 -0
  144. package/templates/shared/bundle-authoring.md +39 -3
  145. package/templates/shared/default-layout.ejs +131 -18
  146. package/templates/shared/design-spec.yaml +2 -3
  147. package/templates/shared/design.registry.base.json +38 -0
  148. package/templates/shared/feature.bundle.yaml +16 -9
  149. package/templates/shared/ir/generated/spec.md +281 -0
  150. package/templates/shared/qa-item.yaml +1 -1
  151. package/templates/shared/tpl-api-contract.md +133 -0
  152. package/templates/shared/tpl-screen-data-model.md +76 -0
  153. package/templates/tests-skeleton/cases/README.md +4 -0
  154. package/templates/tests-skeleton/catalog/locale.yaml +9 -0
  155. package/templates/tests-skeleton/tpl-testcase-plan.md +9 -0
  156. package/harness/docs/skills/api-integration/SKILL.md +0 -110
  157. package/harness/docs/skills/grill-integration-spec/SKILL.md +0 -51
@@ -0,0 +1,176 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import { ENV_TESTS_DOC, resolveTestsDocsRoot } from './flowgrid-tests-docs.mjs';
5
+
6
+ export const ENV_DOCS_ROOT = 'FLOWGRID_DOCS_ROOT';
7
+ export const ENV_PROJECT_ROOT = 'FLOWGRID_PROJECT_ROOT';
8
+ export const ENV_E2E_ROOT = 'FLOWGRID_E2E_ROOT';
9
+
10
+ /**
11
+ * @param {string} [cwd]
12
+ * @returns {object | null}
13
+ */
14
+ export function loadFlowgridConfig(cwd = process.cwd()) {
15
+ const configPath = path.join(path.resolve(cwd), '.flowgrid', 'config.json');
16
+ if (!fs.existsSync(configPath)) return null;
17
+ try {
18
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * @param {string | null | undefined} relOrAbs
26
+ * @param {string} baseDir
27
+ * @returns {string | null}
28
+ */
29
+ function resolveHubPath(relOrAbs, baseDir) {
30
+ if (!relOrAbs || typeof relOrAbs !== 'string' || !relOrAbs.trim()) return null;
31
+ const trimmed = relOrAbs.trim();
32
+ if (path.isAbsolute(trimmed)) return path.resolve(trimmed);
33
+ return path.resolve(baseDir, trimmed);
34
+ }
35
+
36
+ /**
37
+ * Docs hub pointer from config (FE lane first, then BE).
38
+ * @param {object | null | undefined} config
39
+ */
40
+ export function docsRootFromConfig(config) {
41
+ if (!config || config.type === 'Document') return null;
42
+ const rel = config.frontend?.docsRoot ?? config.backend?.docsRoot ?? null;
43
+ return rel || null;
44
+ }
45
+
46
+ /**
47
+ * Tests-docs hub pointer from config.
48
+ * @param {object | null | undefined} config
49
+ */
50
+ export function testsRootFromConfig(config) {
51
+ if (!config || config.type === 'Test') return null;
52
+ const rel = config.frontend?.testsRoot ?? config.backend?.testsRoot ?? null;
53
+ return rel || null;
54
+ }
55
+
56
+ /**
57
+ * E2e automation root from config.
58
+ * @param {object | null | undefined} config
59
+ */
60
+ export function e2eRootFromConfig(config) {
61
+ if (!config) return null;
62
+ const rel = config.frontend?.e2eRoot ?? config.backend?.e2eRoot ?? null;
63
+ return rel || null;
64
+ }
65
+
66
+ /**
67
+ * Strip hub path flags from argv for downstream engines.
68
+ * @param {string[]} argv
69
+ * @returns {{ argv: string[]; docsRoot: string | null; testsDocs: string | null; e2eRoot: string | null; projectRoot: string | null }}
70
+ */
71
+ export function extractFlowgridPathFlags(argv, baseDir = process.cwd()) {
72
+ const out = [];
73
+ let docsRoot = null;
74
+ let testsDocs = null;
75
+ let e2eRoot = null;
76
+ let projectRoot = null;
77
+ const cwd = path.resolve(baseDir);
78
+
79
+ for (let i = 0; i < argv.length; i++) {
80
+ const arg = argv[i];
81
+ if (arg === '--docs-root' && argv[i + 1]) {
82
+ docsRoot = path.resolve(cwd, argv[++i]);
83
+ continue;
84
+ }
85
+ if (arg === '--tests-docs' && argv[i + 1]) {
86
+ testsDocs = path.resolve(cwd, argv[++i]);
87
+ continue;
88
+ }
89
+ if (arg === '--e2e-root' && argv[i + 1]) {
90
+ e2eRoot = path.resolve(cwd, argv[++i]);
91
+ continue;
92
+ }
93
+ if (arg === '--project-root' && argv[i + 1]) {
94
+ projectRoot = path.resolve(cwd, argv[++i]);
95
+ continue;
96
+ }
97
+ out.push(arg);
98
+ }
99
+
100
+ return { argv: out, docsRoot, testsDocs, e2eRoot, projectRoot };
101
+ }
102
+
103
+ /**
104
+ * Resolve docs hub absolute path.
105
+ * Priority: CLI --docs-root → FLOWGRID_DOCS_ROOT → config frontend|backend.docsRoot → Document type = cwd.
106
+ *
107
+ * @param {{ cwd?: string; config?: object | null; argv?: string[]; env?: NodeJS.ProcessEnv }} [opts]
108
+ * @returns {{ docsRoot: string | null; argv: string[]; projectRoot: string }}
109
+ */
110
+ export function resolveFlowgridContext(opts = {}) {
111
+ const cwd = path.resolve(opts.cwd ?? process.cwd());
112
+ const env = opts.env ?? process.env;
113
+ const config = opts.config !== undefined ? opts.config : loadFlowgridConfig(cwd);
114
+ const extracted = extractFlowgridPathFlags(opts.argv ?? [], cwd);
115
+
116
+ const projectRoot = extracted.projectRoot
117
+ ?? (env[ENV_PROJECT_ROOT] ? path.resolve(env[ENV_PROJECT_ROOT]) : cwd);
118
+
119
+ let docsRoot = extracted.docsRoot;
120
+ if (!docsRoot && env[ENV_DOCS_ROOT]) {
121
+ docsRoot = path.resolve(env[ENV_DOCS_ROOT]);
122
+ }
123
+ if (!docsRoot && config?.type === 'Document') {
124
+ docsRoot = projectRoot;
125
+ }
126
+ if (!docsRoot) {
127
+ const rel = docsRootFromConfig(config);
128
+ docsRoot = resolveHubPath(rel, projectRoot);
129
+ }
130
+
131
+ let testsDocs = extracted.testsDocs ?? resolveTestsDocsRoot(null);
132
+ if (!testsDocs) {
133
+ const rel = testsRootFromConfig(config);
134
+ testsDocs = resolveHubPath(rel, projectRoot);
135
+ }
136
+
137
+ let e2eRoot = extracted.e2eRoot;
138
+ if (!e2eRoot && env[ENV_E2E_ROOT]) {
139
+ e2eRoot = path.resolve(env[ENV_E2E_ROOT]);
140
+ }
141
+ if (!e2eRoot) {
142
+ const rel = e2eRootFromConfig(config);
143
+ e2eRoot = resolveHubPath(rel, projectRoot);
144
+ }
145
+
146
+ return {
147
+ projectRoot,
148
+ docsRoot,
149
+ testsDocs,
150
+ e2eRoot,
151
+ argv: extracted.argv,
152
+ config,
153
+ };
154
+ }
155
+
156
+ /**
157
+ * @param {{ cwd?: string; config?: object | null; argv?: string[]; env?: NodeJS.ProcessEnv }} [opts]
158
+ * @returns {string | null}
159
+ */
160
+ export function resolveDocsRoot(opts = {}) {
161
+ return resolveFlowgridContext(opts).docsRoot;
162
+ }
163
+
164
+ /**
165
+ * Apply resolved hub env for child processes (only sets when value known).
166
+ * @param {ReturnType<typeof resolveFlowgridContext>} ctx
167
+ * @param {NodeJS.ProcessEnv} [base]
168
+ */
169
+ export function flowgridContextToEnv(ctx, base = {}) {
170
+ const env = { ...base };
171
+ if (ctx.docsRoot) env[ENV_DOCS_ROOT] = ctx.docsRoot;
172
+ if (ctx.testsDocs) env[ENV_TESTS_DOC] = ctx.testsDocs;
173
+ if (ctx.e2eRoot) env[ENV_E2E_ROOT] = ctx.e2eRoot;
174
+ env[ENV_PROJECT_ROOT] = ctx.projectRoot;
175
+ return env;
176
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * audit-api-gaps.mjs
5
- * Zero-dependency static audit script for FlowGrid API Contracts (backend-api.bundle.yaml, backend-api-integration.yaml).
5
+ * Zero-dependency static audit for API contracts — target SSOT: `01-backend-spec.yaml` (legacy bundle-with-spec.api still parseable).
6
6
  * Ensures 100% adherence to API payload rules, SLA, async events, and resilience policies.
7
7
  */
8
8
 
@@ -13,13 +13,15 @@
13
13
  * pageType: list | create | detail | admin-crud | auth | change-password | public | not-found | error
14
14
  * If --type is omitted, script auto-detects from codegen.profile or design.shell.tag.
15
15
  *
16
- * Output: JSON with gaps[] (missing required) + confirms[] (optional, agent must ask member).
17
- * UX affordance checks (flowgrid-ux-common) merge in with category "ux" and codes UX_* / CONFIRM_UX_*.
16
+ * Output: JSON with gaps[] (missing required) + confirms[] (optional wizard) + warnings[] (quality — never blocks split).
17
+ * UX affordance: category "ux" (UX_* / CONFIRM_UX_*). DB tables: category "db" (CONFIRM_DB_*).
18
18
  */
19
19
 
20
20
  import fs from 'fs';
21
21
  import path from 'path';
22
+ import { parse } from 'yaml';
22
23
  import { auditUxAffordance } from './audit-ux-affordance.mjs';
24
+ import { auditDbTables, resolveBackendSpecForBundle } from './audit-db-tables.mjs';
23
25
 
24
26
  // ---------------------------------------------------------------------------
25
27
  // Helpers
@@ -111,6 +113,7 @@ function hasMutationActions(rawText) {
111
113
  function auditBundleContent(rawText, filePath, pageType) {
112
114
  const gaps = [];
113
115
  const confirms = [];
116
+ const warnings = [];
114
117
 
115
118
  function addGap(code, severity, fieldPath, message, suggestedFix) {
116
119
  gaps.push({ code, severity, type: 'gap', path: fieldPath, message, suggestedFix });
@@ -127,6 +130,17 @@ function auditBundleContent(rawText, filePath, pageType) {
127
130
  });
128
131
  }
129
132
 
133
+ function addWarning(code, fieldPath, message, suggestedFix) {
134
+ warnings.push({
135
+ code,
136
+ type: 'warning',
137
+ category: 'quality',
138
+ path: fieldPath,
139
+ message,
140
+ suggestedFix
141
+ });
142
+ }
143
+
130
144
  // =========================================================================
131
145
  // ALL PAGES — Required fields
132
146
  // =========================================================================
@@ -528,6 +542,51 @@ function auditBundleContent(rawText, filePath, pageType) {
528
542
  const uxGaps = gaps.filter((g) => g.category === 'ux').length;
529
543
  const uxConfirms = confirms.filter((c) => c.category === 'ux').length;
530
544
 
545
+ // =========================================================================
546
+ // Quality warnings (non-blocking — does not affect totalGaps for structure)
547
+ // =========================================================================
548
+
549
+ if (!has(rawText, 'successMetrics:')) {
550
+ addWarning(
551
+ 'WARN_NO_SUCCESS_METRICS',
552
+ 'successMetrics',
553
+ 'successMetrics not declared — add when PO has measurable or qualitative targets.',
554
+ 'Declare successMetrics: | with 1–3 bullets (see bundle-authoring.md).'
555
+ );
556
+ }
557
+
558
+ if (!has(rawText, 'nonGoals:')) {
559
+ addWarning(
560
+ 'WARN_NO_NON_GOALS',
561
+ 'nonGoals',
562
+ 'nonGoals not declared — clarify out-of-scope for this screen/phase.',
563
+ 'Declare nonGoals: | with explicit non-scope bullets.'
564
+ );
565
+ }
566
+
567
+ const placeholderCue = /\[[^\]]{8,}\]/;
568
+ if (has(rawText, 'summary:') && placeholderCue.test(rawText)) {
569
+ addWarning(
570
+ 'WARN_SUMMARY_PLACEHOLDER',
571
+ 'summary',
572
+ 'Summary or related prose still contains bracket placeholders `[...]`.',
573
+ 'Replace template brackets with real Vietnamese business copy before stakeholder sign-off.'
574
+ );
575
+ }
576
+
577
+ if (
578
+ has(rawText, 'messages:') &&
579
+ has(rawText, 'required:') &&
580
+ !hasAny(rawText, ['Trường này', 'bắt buộc', 'không được để trống', 'Vui lòng'])
581
+ ) {
582
+ addWarning(
583
+ 'WARN_VALIDATION_MSG_GENERIC',
584
+ 'design.sections',
585
+ 'Validation messages may still use generic English — prefer explicit Vietnamese copy per field.',
586
+ 'Set messages.* on each validated field (see bundle-authoring.md).'
587
+ );
588
+ }
589
+
531
590
  // =========================================================================
532
591
  // Result
533
592
  // =========================================================================
@@ -537,13 +596,15 @@ function auditBundleContent(rawText, filePath, pageType) {
537
596
  detectedType: pageType,
538
597
  totalGaps: gaps.length,
539
598
  totalConfirms: confirms.length,
599
+ totalWarnings: warnings.length,
540
600
  uxAffordanceGaps: uxGaps,
541
601
  uxAffordanceConfirms: uxConfirms,
542
602
  criticalGaps: gaps.filter(g => g.severity === 'critical').length,
543
603
  warningGaps: gaps.filter(g => g.severity === 'warning').length,
544
604
  infoGaps: gaps.filter(g => g.severity === 'info').length,
545
605
  gaps,
546
- confirms
606
+ confirms,
607
+ warnings
547
608
  };
548
609
  }
549
610
 
@@ -579,4 +640,32 @@ if (!fs.existsSync(targetFile)) {
579
640
  const rawText = fs.readFileSync(targetFile, 'utf8');
580
641
  const pageType = typeArg || detectPageType(rawText);
581
642
  const report = auditBundleContent(rawText, targetFile, pageType);
643
+
644
+ try {
645
+ const bundle = parse(rawText);
646
+ const backendResolved = resolveBackendSpecForBundle(targetFile, null);
647
+ const backendText =
648
+ backendResolved && fs.existsSync(backendResolved)
649
+ ? fs.readFileSync(backendResolved, 'utf8')
650
+ : '';
651
+ const dbReport = auditDbTables({
652
+ bundle,
653
+ backendText,
654
+ backendSpecFound: !!(backendResolved && fs.existsSync(backendResolved)),
655
+ filePath: targetFile,
656
+ });
657
+ report.confirms.push(...dbReport.confirms);
658
+ report.warnings.push(...dbReport.warnings);
659
+ report.totalConfirms = report.confirms.length;
660
+ report.totalWarnings = report.warnings.length;
661
+ report.dbTables = {
662
+ tables: dbReport.tables,
663
+ bindingCount: dbReport.bindings?.length ?? 0,
664
+ dbConfirms: dbReport.confirms?.length ?? 0,
665
+ backendSpec: backendResolved ? path.relative(process.cwd(), backendResolved) : null,
666
+ };
667
+ } catch {
668
+ // YAML parse errors are handled elsewhere; skip DB pass
669
+ }
670
+
582
671
  console.log(JSON.stringify(report, null, 2));