sumulige-claude 1.1.2 → 1.2.0
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/.claude/hooks/code-formatter.cjs +7 -2
- package/.claude/hooks/multi-session.cjs +9 -3
- package/.claude/hooks/pre-commit.cjs +0 -0
- package/.claude/hooks/pre-push.cjs +0 -0
- package/.claude/hooks/project-kickoff.cjs +22 -11
- package/.claude/hooks/rag-skill-loader.cjs +7 -0
- package/.claude/hooks/thinking-silent.cjs +9 -3
- package/.claude/hooks/todo-manager.cjs +19 -13
- package/.claude/hooks/verify-work.cjs +10 -4
- package/.claude/quality-gate.json +9 -3
- package/.claude/settings.local.json +16 -1
- package/.claude/templates/hooks/README.md +302 -0
- package/.claude/templates/hooks/hook.sh.template +94 -0
- package/.claude/templates/hooks/user-prompt-submit.cjs.template +116 -0
- package/.claude/templates/hooks/user-response-submit.cjs.template +94 -0
- package/.claude/templates/hooks/validate.js +173 -0
- package/.claude/workflow/document-scanner.js +426 -0
- package/.claude/workflow/knowledge-engine.js +941 -0
- package/.claude/workflow/notebooklm/browser.js +1028 -0
- package/.claude/workflow/phases/phase1-research.js +578 -0
- package/.claude/workflow/phases/phase1-research.ts +465 -0
- package/.claude/workflow/phases/phase2-approve.js +722 -0
- package/.claude/workflow/phases/phase3-plan.js +1200 -0
- package/.claude/workflow/phases/phase4-develop.js +894 -0
- package/.claude/workflow/search-cache.js +230 -0
- package/.claude/workflow/templates/approval.md +315 -0
- package/.claude/workflow/templates/development.md +377 -0
- package/.claude/workflow/templates/planning.md +328 -0
- package/.claude/workflow/templates/research.md +250 -0
- package/.claude/workflow/types.js +37 -0
- package/.claude/workflow/web-search.js +278 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +176 -0
- package/CHANGELOG.md +7 -14
- package/cli.js +20 -0
- package/config/quality-gate.json +9 -3
- package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +36 -0
- package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +36 -0
- package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +36 -0
- package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +36 -0
- package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +36 -0
- package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +36 -0
- package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +36 -0
- package/development/knowledge-base/.index.clean.json +0 -0
- package/development/knowledge-base/.index.json +486 -0
- package/development/knowledge-base/test-best-practices.md +29 -0
- package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +226 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +345 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +284 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +14 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +35 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +34 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +5 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +60 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +25 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +70 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +48 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +20 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +21 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +226 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +345 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +284 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +14 -0
- package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +178 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +377 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +442 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +800 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +625 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +830 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +957 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +381 -0
- package/development/todos/.state.json +14 -1
- package/development/todos/INDEX.md +31 -73
- package/development/todos/completed/develop/local-knowledge-index.md +85 -0
- package/development/todos/{active → completed/develop}/todo-system.md +13 -3
- package/development/todos/completed/develop/web-search-integration.md +83 -0
- package/development/todos/completed/test/phase1-e2e-test.md +103 -0
- package/lib/commands.js +388 -0
- package/package.json +3 -2
- package/tests/config-manager.test.js +677 -0
- package/tests/config-validator.test.js +436 -0
- package/tests/errors.test.js +477 -0
- package/tests/manual/phase1-e2e.sh +389 -0
- package/tests/manual/phase2-test-cases.md +311 -0
- package/tests/manual/phase3-test-cases.md +309 -0
- package/tests/manual/phase4-test-cases.md +414 -0
- package/tests/manual/test-cases.md +417 -0
- package/tests/quality-gate.test.js +679 -0
- package/tests/quality-rules.test.js +619 -0
- package/tests/version-check.test.js +75 -0
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
# Data Model Design Document
|
|
2
|
+
|
|
3
|
+
**Project**: proj_mkh8zddd_dhamf (AI 代码审查工具)
|
|
4
|
+
**Date**: 1/17/2026
|
|
5
|
+
**Phase**: 3 - Planning
|
|
6
|
+
**Status**: In Progress
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Executive Summary
|
|
11
|
+
|
|
12
|
+
本文档定义 AI 代码审查工具的核心数据结构,包括 Issue 模型、ScanResult 数据结构和报告格式。
|
|
13
|
+
|
|
14
|
+
**设计原则**:
|
|
15
|
+
- **类型安全**: Go 结构体定义清晰的类型
|
|
16
|
+
- **可序列化**: 支持 JSON/YAML 序列化
|
|
17
|
+
- **可扩展**: 预留扩展字段
|
|
18
|
+
- **向后兼容**: 新字段不影响旧版本
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 1. Core Data Types
|
|
23
|
+
|
|
24
|
+
### 1.1 Severity Levels
|
|
25
|
+
|
|
26
|
+
```go
|
|
27
|
+
// Severity represents the severity level of an issue
|
|
28
|
+
type Severity string
|
|
29
|
+
|
|
30
|
+
const (
|
|
31
|
+
SeverityCritical Severity = "critical"
|
|
32
|
+
SeverityHigh Severity = "high"
|
|
33
|
+
SeverityMedium Severity = "medium"
|
|
34
|
+
SeverityLow Severity = "low"
|
|
35
|
+
SeverityInfo Severity = "info"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
// Order returns the numeric ordering for comparison
|
|
39
|
+
func (s Severity) Order() int {
|
|
40
|
+
switch s {
|
|
41
|
+
case SeverityCritical:
|
|
42
|
+
return 4
|
|
43
|
+
case SeverityHigh:
|
|
44
|
+
return 3
|
|
45
|
+
case SeverityMedium:
|
|
46
|
+
return 2
|
|
47
|
+
case SeverityLow:
|
|
48
|
+
return 1
|
|
49
|
+
case SeverityInfo:
|
|
50
|
+
return 0
|
|
51
|
+
default:
|
|
52
|
+
return -1
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Color returns the terminal color for this severity
|
|
57
|
+
func (s Severity) Color() string {
|
|
58
|
+
switch s {
|
|
59
|
+
case SeverityCritical:
|
|
60
|
+
return "red"
|
|
61
|
+
case SeverityHigh:
|
|
62
|
+
return "lightRed"
|
|
63
|
+
case SeverityMedium:
|
|
64
|
+
return "yellow"
|
|
65
|
+
case SeverityLow:
|
|
66
|
+
return "lightBlue"
|
|
67
|
+
case SeverityInfo:
|
|
68
|
+
return "blue"
|
|
69
|
+
default:
|
|
70
|
+
return "white"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 1.2 Issue Categories
|
|
76
|
+
|
|
77
|
+
```go
|
|
78
|
+
// Category represents the type of issue
|
|
79
|
+
type Category string
|
|
80
|
+
|
|
81
|
+
const (
|
|
82
|
+
CategorySecurity Category = "security"
|
|
83
|
+
CategoryQuality Category = "quality"
|
|
84
|
+
CategoryPerformance Category = "performance"
|
|
85
|
+
CategoryStyle Category = "style"
|
|
86
|
+
CategoryDocumentation Category = "documentation"
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 1.3 Confidence Levels
|
|
91
|
+
|
|
92
|
+
```go
|
|
93
|
+
// Confidence represents the confidence level of the detection
|
|
94
|
+
type Confidence string
|
|
95
|
+
|
|
96
|
+
const (
|
|
97
|
+
ConfidenceHigh Confidence = "high"
|
|
98
|
+
ConfidenceMedium Confidence = "medium"
|
|
99
|
+
ConfidenceLow Confidence = "low"
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 1.4 Language Support
|
|
104
|
+
|
|
105
|
+
```go
|
|
106
|
+
// Language represents supported programming languages
|
|
107
|
+
type Language string
|
|
108
|
+
|
|
109
|
+
const (
|
|
110
|
+
LanguagePython Language = "python"
|
|
111
|
+
LanguageJavaScript Language = "javascript"
|
|
112
|
+
LanguageTypeScript Language = "typescript"
|
|
113
|
+
LanguageJSX Language = "jsx"
|
|
114
|
+
LanguageTSX Language = "tsx"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
// Extension returns the common file extension for this language
|
|
118
|
+
func (l Language) Extension() string {
|
|
119
|
+
switch l {
|
|
120
|
+
case LanguagePython:
|
|
121
|
+
return ".py"
|
|
122
|
+
case LanguageJavaScript:
|
|
123
|
+
return ".js"
|
|
124
|
+
case LanguageTypeScript:
|
|
125
|
+
return ".ts"
|
|
126
|
+
case LanguageJSX:
|
|
127
|
+
return ".jsx"
|
|
128
|
+
case LanguageTSX:
|
|
129
|
+
return ".tsx"
|
|
130
|
+
default:
|
|
131
|
+
return ""
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 2. Issue Data Model
|
|
139
|
+
|
|
140
|
+
### 2.1 Issue Structure
|
|
141
|
+
|
|
142
|
+
```go
|
|
143
|
+
// Issue represents a single code issue found during scanning
|
|
144
|
+
type Issue struct {
|
|
145
|
+
// Core identifiers
|
|
146
|
+
ID string `json:"id" yaml:"id"` // Unique issue ID (e.g., "issue_abc123")
|
|
147
|
+
RuleID string `json:"rule_id" yaml:"rule_id"` // Rule that generated this issue
|
|
148
|
+
ScanID string `json:"scan_id" yaml:"scan_id"` // Scan that found this issue
|
|
149
|
+
|
|
150
|
+
// Classification
|
|
151
|
+
Severity Severity `json:"severity" yaml:"severity"` // Issue severity
|
|
152
|
+
Category Category `json:"category" yaml:"category"` // Issue category
|
|
153
|
+
Confidence Confidence `json:"confidence" yaml:"confidence"` // Detection confidence
|
|
154
|
+
|
|
155
|
+
// Description
|
|
156
|
+
Title string `json:"title" yaml:"title"` // Short title
|
|
157
|
+
Description string `json:"description" yaml:"description"` // Detailed description
|
|
158
|
+
Message string `json:"message" yaml:"message"` // Formatted message for display
|
|
159
|
+
|
|
160
|
+
// Location
|
|
161
|
+
File string `json:"file" yaml:"file"` // Relative file path
|
|
162
|
+
Line int `json:"line" yaml:"line"` // Start line (1-indexed)
|
|
163
|
+
Column int `json:"column" yaml:"column"` // Start column (1-indexed)
|
|
164
|
+
EndLine int `json:"end_line" yaml:"end_line"` // End line
|
|
165
|
+
EndColumn int `json:"end_column" yaml:"end_column"` // End column
|
|
166
|
+
|
|
167
|
+
// Code context
|
|
168
|
+
CodeSnippet string `json:"code_snippet,omitempty" yaml:"code_snippet,omitempty"`
|
|
169
|
+
ContextBefore []string `json:"context_before,omitempty" yaml:"context_before,omitempty"`
|
|
170
|
+
ContextAfter []string `json:"context_after,omitempty" yaml:"context_after,omitempty"`
|
|
171
|
+
|
|
172
|
+
// Fix suggestions
|
|
173
|
+
Suggestion string `json:"suggestion,omitempty" yaml:"suggestion,omitempty"`
|
|
174
|
+
SuggestedCode string `json:"suggested_code,omitempty" yaml:"suggested_code,omitempty"`
|
|
175
|
+
Fixable bool `json:"fixable" yaml:"fixable"`
|
|
176
|
+
AutoFixAvailable bool `json:"auto_fix_available,omitempty" yaml:"auto_fix_available,omitempty"`
|
|
177
|
+
|
|
178
|
+
// References
|
|
179
|
+
References []string `json:"references,omitempty" yaml:"references,omitempty"`
|
|
180
|
+
CWE string `json:"cwe,omitempty" yaml:"cwe,omitempty"` // CWE ID if applicable
|
|
181
|
+
OWASP string `json:"owasp,omitempty" yaml:"owasp,omitempty"` // OWASP category if applicable
|
|
182
|
+
|
|
183
|
+
// Metadata
|
|
184
|
+
Language Language `json:"language,omitempty" yaml:"language,omitempty"`
|
|
185
|
+
AIEnhanced bool `json:"ai_enhanced" yaml:"ai_enhanced"`
|
|
186
|
+
AIModel string `json:"ai_model,omitempty" yaml:"ai_model,omitempty"`
|
|
187
|
+
CreatedAt time.Time `json:"created_at" yaml:"created_at"`
|
|
188
|
+
|
|
189
|
+
// Extension fields
|
|
190
|
+
Extra map[string]interface{} `json:"extra,omitempty" yaml:"extra,omitempty"`
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 2.2 Issue Methods
|
|
195
|
+
|
|
196
|
+
```go
|
|
197
|
+
// LocationString returns a formatted location string
|
|
198
|
+
func (i *Issue) LocationString() string {
|
|
199
|
+
if i.EndLine > i.Line {
|
|
200
|
+
return fmt.Sprintf("%s:%d-%d", i.File, i.Line, i.EndLine)
|
|
201
|
+
}
|
|
202
|
+
return fmt.Sprintf("%s:%d", i.File, i.Line)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// FullLocationString returns a detailed location string
|
|
206
|
+
func (i *Issue) FullLocationString() string {
|
|
207
|
+
return fmt.Sprintf("%s:%d:%d", i.File, i.Line, i.Column)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// String returns a formatted string representation
|
|
211
|
+
func (i *Issue) String() string {
|
|
212
|
+
return fmt.Sprintf("[%s] %s: %s",
|
|
213
|
+
strings.ToUpper(string(i.Severity)),
|
|
214
|
+
i.FullLocationString(),
|
|
215
|
+
i.Title,
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// IsSecurity returns true if this is a security issue
|
|
220
|
+
func (i *Issue) IsSecurity() bool {
|
|
221
|
+
return i.Category == CategorySecurity
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Hash returns a deterministic hash for deduplication
|
|
225
|
+
func (i *Issue) Hash() string {
|
|
226
|
+
data := fmt.Sprintf("%s:%s:%d:%d",
|
|
227
|
+
i.RuleID,
|
|
228
|
+
i.File,
|
|
229
|
+
i.Line,
|
|
230
|
+
i.Column,
|
|
231
|
+
)
|
|
232
|
+
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 2.3 Issue Collection
|
|
237
|
+
|
|
238
|
+
```go
|
|
239
|
+
// Issues is a collection of issues with utility methods
|
|
240
|
+
type Issues []Issue
|
|
241
|
+
|
|
242
|
+
// FilterBySeverity returns issues with severity >= min
|
|
243
|
+
func (is Issues) FilterBySeverity(min Severity) Issues {
|
|
244
|
+
var result Issues
|
|
245
|
+
for _, issue := range is {
|
|
246
|
+
if issue.Severity.Order() >= min.Order() {
|
|
247
|
+
result = append(result, issue)
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return result
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// FilterByCategory returns issues in the specified category
|
|
254
|
+
func (is Issues) FilterByCategory(cat Category) Issues {
|
|
255
|
+
var result Issues
|
|
256
|
+
for _, issue := range is {
|
|
257
|
+
if issue.Category == cat {
|
|
258
|
+
result = append(result, issue)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return result
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// FilterByFile returns issues in the specified file
|
|
265
|
+
func (is Issues) FilterByFile(file string) Issues {
|
|
266
|
+
var result Issues
|
|
267
|
+
for _, issue := range is {
|
|
268
|
+
if issue.File == file {
|
|
269
|
+
result = append(result, issue)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return result
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// BySeverity sorts issues by severity (highest first)
|
|
276
|
+
func (is Issues) BySeverity() Issues {
|
|
277
|
+
sorted := make(Issues, len(is))
|
|
278
|
+
copy(sorted, is)
|
|
279
|
+
sort.Slice(sorted, func(i, j int) bool {
|
|
280
|
+
return sorted[i].Severity.Order() > sorted[j].Severity.Order()
|
|
281
|
+
})
|
|
282
|
+
return sorted
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// GroupByFile groups issues by file
|
|
286
|
+
func (is Issues) GroupByFile() map[string]Issues {
|
|
287
|
+
result := make(map[string]Issues)
|
|
288
|
+
for _, issue := range is {
|
|
289
|
+
result[issue.File] = append(result[issue.File], issue)
|
|
290
|
+
}
|
|
291
|
+
return result
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Deduplicate removes duplicate issues based on hash
|
|
295
|
+
func (is Issues) Deduplicate() Issues {
|
|
296
|
+
seen := make(map[string]bool)
|
|
297
|
+
var result Issues
|
|
298
|
+
for _, issue := range is {
|
|
299
|
+
hash := issue.Hash()
|
|
300
|
+
if !seen[hash] {
|
|
301
|
+
seen[hash] = true
|
|
302
|
+
result = append(result, issue)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return result
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 3. ScanResult Data Structure
|
|
312
|
+
|
|
313
|
+
### 3.1 ScanResult Structure
|
|
314
|
+
|
|
315
|
+
```go
|
|
316
|
+
// ScanResult represents the complete results of a code scan
|
|
317
|
+
type ScanResult struct {
|
|
318
|
+
// Scan metadata
|
|
319
|
+
ScanID string `json:"scan_id" yaml:"scan_id"`
|
|
320
|
+
Version string `json:"version" yaml:"version"` // Tool version
|
|
321
|
+
Timestamp time.Time `json:"timestamp" yaml:"timestamp"`
|
|
322
|
+
Duration time.Duration `json:"duration" yaml:"duration"`
|
|
323
|
+
|
|
324
|
+
// Scan configuration
|
|
325
|
+
Config ScanConfig `json:"config,omitempty" yaml:"config,omitempty"`
|
|
326
|
+
|
|
327
|
+
// Files scanned
|
|
328
|
+
FilesScanned int `json:"files_scanned" yaml:"files_scanned"`
|
|
329
|
+
FilesSkipped int `json:"files_skipped" yaml:"files_skipped"`
|
|
330
|
+
Files []FileResult `json:"files,omitempty" yaml:"files,omitempty"`
|
|
331
|
+
|
|
332
|
+
// Issues
|
|
333
|
+
Issues Issues `json:"issues" yaml:"issues"`
|
|
334
|
+
Summary IssueSummary `json:"summary" yaml:"summary"`
|
|
335
|
+
|
|
336
|
+
// Performance metrics
|
|
337
|
+
Performance PerformanceMetrics `json:"performance" yaml:"performance"`
|
|
338
|
+
|
|
339
|
+
// AI usage (if applicable)
|
|
340
|
+
AIUsage *AIUsage `json:"ai_usage,omitempty" yaml:"ai_usage,omitempty"`
|
|
341
|
+
|
|
342
|
+
// Extension fields
|
|
343
|
+
Extra map[string]interface{} `json:"extra,omitempty" yaml:"extra,omitempty"`
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### 3.2 Scan Configuration Snapshot
|
|
348
|
+
|
|
349
|
+
```go
|
|
350
|
+
// ScanConfig captures the configuration used for a scan
|
|
351
|
+
type ScanConfig struct {
|
|
352
|
+
Path string `json:"path" yaml:"path"`
|
|
353
|
+
Include []string `json:"include,omitempty" yaml:"include,omitempty"`
|
|
354
|
+
Exclude []string `json:"exclude,omitempty" yaml:"exclude,omitempty"`
|
|
355
|
+
MinSeverity Severity `json:"min_severity" yaml:"min_severity"`
|
|
356
|
+
Recursive bool `json:"recursive" yaml:"recursive"`
|
|
357
|
+
DiffMode bool `json:"diff_mode" yaml:"diff_mode"`
|
|
358
|
+
AIEnabled bool `json:"ai_enabled" yaml:"ai_enabled"`
|
|
359
|
+
Jobs int `json:"jobs" yaml:"jobs"`
|
|
360
|
+
RulesEnabled []string `json:"rules_enabled,omitempty" yaml:"rules_enabled,omitempty"`
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 3.3 File Result
|
|
365
|
+
|
|
366
|
+
```go
|
|
367
|
+
// FileResult represents the scan result for a single file
|
|
368
|
+
type FileResult struct {
|
|
369
|
+
Path string `json:"path" yaml:"path"`
|
|
370
|
+
Language Language `json:"language" yaml:"language"`
|
|
371
|
+
Lines int `json:"lines" yaml:"lines"`
|
|
372
|
+
Issues Issues `json:"issues" yaml:"issues"`
|
|
373
|
+
Error string `json:"error,omitempty" yaml:"error,omitempty"`
|
|
374
|
+
Skipped bool `json:"skipped" yaml:"skipped"`
|
|
375
|
+
SkipReason string `json:"skip_reason,omitempty" yaml:"skip_reason,omitempty"`
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 3.4 Issue Summary
|
|
380
|
+
|
|
381
|
+
```go
|
|
382
|
+
// IssueSummary provides a summary of issues found
|
|
383
|
+
type IssueSummary struct {
|
|
384
|
+
Total int `json:"total" yaml:"total"`
|
|
385
|
+
BySeverity map[Severity]int `json:"by_severity" yaml:"by_severity"`
|
|
386
|
+
ByCategory map[Category]int `json:"by_category" yaml:"by_category"`
|
|
387
|
+
ByRule map[string]int `json:"by_rule,omitempty" yaml:"by_rule,omitempty"`
|
|
388
|
+
ByFile map[string]int `json:"by_file,omitempty" yaml:"by_file,omitempty"`
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// NewIssueSummary creates a summary from a list of issues
|
|
392
|
+
func NewIssueSummary(issues Issues) IssueSummary {
|
|
393
|
+
summary := IssueSummary{
|
|
394
|
+
Total: len(issues),
|
|
395
|
+
BySeverity: make(map[Severity]int),
|
|
396
|
+
ByCategory: make(map[Category]int),
|
|
397
|
+
ByRule: make(map[string]int),
|
|
398
|
+
ByFile: make(map[string]int),
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
for _, issue := range issues {
|
|
402
|
+
summary.BySeverity[issue.Severity]++
|
|
403
|
+
summary.ByCategory[issue.Category]++
|
|
404
|
+
summary.ByRule[issue.RuleID]++
|
|
405
|
+
summary.ByFile[issue.File]++
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return summary
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 3.5 Performance Metrics
|
|
413
|
+
|
|
414
|
+
```go
|
|
415
|
+
// PerformanceMetrics captures scan performance data
|
|
416
|
+
type PerformanceMetrics struct {
|
|
417
|
+
// Timing
|
|
418
|
+
TotalDuration time.Duration `json:"total_duration" yaml:"total_duration"`
|
|
419
|
+
ParseDuration time.Duration `json:"parse_duration" yaml:"parse_duration"`
|
|
420
|
+
RuleDuration time.Duration `json:"rule_duration" yaml:"rule_duration"`
|
|
421
|
+
LLMDuration time.Duration `json:"llm_duration,omitempty" yaml:"llm_duration,omitempty"`
|
|
422
|
+
|
|
423
|
+
// Throughput
|
|
424
|
+
FilesPerSecond float64 `json:"files_per_second" yaml:"files_per_second"`
|
|
425
|
+
LinesPerSecond float64 `json:"lines_per_second" yaml:"lines_per_second"`
|
|
426
|
+
IssuesPerSecond float64 `json:"issues_per_second" yaml:"issues_per_second"`
|
|
427
|
+
|
|
428
|
+
// Cache effectiveness
|
|
429
|
+
CacheHitRate float64 `json:"cache_hit_rate" yaml:"cache_hit_rate"`
|
|
430
|
+
|
|
431
|
+
// Resource usage
|
|
432
|
+
MemoryUsedMB int64 `json:"memory_used_mb" yaml:"memory_used_mb"`
|
|
433
|
+
CPUUsagePercent float64 `json:"cpu_usage_percent,omitempty" yaml:"cpu_usage_percent,omitempty"`
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 3.6 AI Usage
|
|
438
|
+
|
|
439
|
+
```go
|
|
440
|
+
// AIUsage captures AI-related usage data
|
|
441
|
+
type AIUsage struct {
|
|
442
|
+
Enabled bool `json:"enabled" yaml:"enabled"`
|
|
443
|
+
Provider string `json:"provider" yaml:"provider"`
|
|
444
|
+
Model string `json:"model" yaml:"model"`
|
|
445
|
+
|
|
446
|
+
// API calls
|
|
447
|
+
TotalRequests int `json:"total_requests" yaml:"total_requests"`
|
|
448
|
+
SuccessfulRequests int `json:"successful_requests" yaml:"successful_requests"`
|
|
449
|
+
FailedRequests int `json:"failed_requests" yaml:"failed_requests"`
|
|
450
|
+
|
|
451
|
+
// Tokens
|
|
452
|
+
TotalTokens int `json:"total_tokens" yaml:"total_tokens"`
|
|
453
|
+
InputTokens int `json:"input_tokens" yaml:"input_tokens"`
|
|
454
|
+
OutputTokens int `json:"output_tokens" yaml:"output_tokens"`
|
|
455
|
+
|
|
456
|
+
// Cost
|
|
457
|
+
EstimatedCostUSD float64 `json:"estimated_cost_usd" yaml:"estimated_cost_usd"`
|
|
458
|
+
|
|
459
|
+
// Issues enhanced by AI
|
|
460
|
+
IssuesEnhanced int `json:"issues_enhanced" yaml:"issues_enhanced"`
|
|
461
|
+
IssuesFound int `json:"issues_found" yaml:"issues_found"`
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## 4. Report Formats
|
|
468
|
+
|
|
469
|
+
### 4.1 Report Metadata
|
|
470
|
+
|
|
471
|
+
```go
|
|
472
|
+
// ReportMetadata contains metadata about a report
|
|
473
|
+
type ReportMetadata struct {
|
|
474
|
+
GeneratedAt time.Time `json:"generated_at" yaml:"generated_at"`
|
|
475
|
+
GeneratedBy string `json:"generated_by" yaml:"generated_by"` // Tool version
|
|
476
|
+
ScanID string `json:"scan_id" yaml:"scan_id"`
|
|
477
|
+
ProjectPath string `json:"project_path" yaml:"project_path"`
|
|
478
|
+
GitBranch string `json:"git_branch,omitempty" yaml:"git_branch,omitempty"`
|
|
479
|
+
GitCommit string `json:"git_commit,omitempty" yaml:"git_commit,omitempty"`
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### 4.2 Trend Report
|
|
484
|
+
|
|
485
|
+
```go
|
|
486
|
+
// TrendReport shows quality trends over time
|
|
487
|
+
type TrendReport struct {
|
|
488
|
+
Metadata ReportMetadata `json:"metadata" yaml:"metadata"`
|
|
489
|
+
Period Period `json:"period" yaml:"period"`
|
|
490
|
+
Current Snapshot `json:"current" yaml:"current"`
|
|
491
|
+
Previous Snapshot `json:"previous" yaml:"previous"`
|
|
492
|
+
Delta TrendDelta `json:"delta" yaml:"delta"`
|
|
493
|
+
History []Snapshot `json:"history,omitempty" yaml:"history,omitempty"`
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Period defines the time period for the trend report
|
|
497
|
+
type Period struct {
|
|
498
|
+
Start time.Time `json:"start" yaml:"start"`
|
|
499
|
+
End time.Time `json:"end" yaml:"end"`
|
|
500
|
+
Days int `json:"days" yaml:"days"`
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Snapshot represents a point-in-time state
|
|
504
|
+
type Snapshot struct {
|
|
505
|
+
Date time.Time `json:"date" yaml:"date"`
|
|
506
|
+
ScanID string `json:"scan_id" yaml:"scan_id"`
|
|
507
|
+
Summary IssueSummary `json:"summary" yaml:"summary"`
|
|
508
|
+
Files int `json:"files" yaml:"files"`
|
|
509
|
+
Lines int `json:"lines" yaml:"lines"`
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// TrendDelta shows changes between snapshots
|
|
513
|
+
type TrendDelta struct {
|
|
514
|
+
TotalIssues int `json:"total_issues" yaml:"total_issues"`
|
|
515
|
+
CriticalDelta int `json:"critical_delta" yaml:"critical_delta"`
|
|
516
|
+
HighDelta int `json:"high_delta" yaml:"high_delta"`
|
|
517
|
+
MediumDelta int `json:"medium_delta" yaml:"medium_delta"`
|
|
518
|
+
LowDelta int `json:"low_delta" yaml:"low_delta"`
|
|
519
|
+
PercentChange float64 `json:"percent_change" yaml:"percent_change"`
|
|
520
|
+
Direction string `json:"direction" yaml:"direction"` // "up", "down", "same"
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### 4.3 SARIF Report
|
|
525
|
+
|
|
526
|
+
```go
|
|
527
|
+
// SARIFReport is the Static Analysis Results Interchange Format
|
|
528
|
+
// See: https://sarifweb.azurewebsites.net/
|
|
529
|
+
type SARIFReport struct {
|
|
530
|
+
Version string `json:"version"`
|
|
531
|
+
Schema string `json:"$schema"`
|
|
532
|
+
Runs []SARIFRun `json:"runs"`
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
type SARIFRun struct {
|
|
536
|
+
Tool SARIFTool `json:"tool"`
|
|
537
|
+
Results []SARIFResult `json:"results"`
|
|
538
|
+
Invocations []SARIFInvocation `json:"invocations,omitempty"`
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
type SARIFTool struct {
|
|
542
|
+
Driver SARIFToolDriver `json:"driver"`
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
type SARIFToolDriver struct {
|
|
546
|
+
Name string `json:"name"`
|
|
547
|
+
Version string `json:"version"`
|
|
548
|
+
InformationURI string `json:"informationUri,omitempty"`
|
|
549
|
+
Rules []SARIFRule `json:"rules,omitempty"`
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
type SARIFRule struct {
|
|
553
|
+
ID string `json:"id"`
|
|
554
|
+
Name string `json:"name,omitempty"`
|
|
555
|
+
ShortDescription SARIFMessage `json:"shortDescription"`
|
|
556
|
+
FullDescription SARIFMessage `json:"fullDescription,omitempty"`
|
|
557
|
+
HelpURI string `json:"helpUri,omitempty"`
|
|
558
|
+
Properties map[string]interface{} `json:"properties,omitempty"`
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
type SARIFResult struct {
|
|
562
|
+
RuleID string `json:"ruleId"`
|
|
563
|
+
Level string `json:"level"` // error, warning, note
|
|
564
|
+
Message SARIFMessage `json:"message"`
|
|
565
|
+
Locations []SARIFLocation `json:"locations"`
|
|
566
|
+
Fixes []SARIFFix `json:"fixes,omitempty"`
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
type SARIFMessage struct {
|
|
570
|
+
Text string `json:"text"`
|
|
571
|
+
Markdown string `json:"markdown,omitempty"`
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
type SARIFLocation struct {
|
|
575
|
+
PhysicalLocation SARIFPhysicalLocation `json:"physicalLocation"`
|
|
576
|
+
LogicalLocations []SARIFLogicalLocation `json:"logicalLocations,omitempty"`
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
type SARIFPhysicalLocation struct {
|
|
580
|
+
ArtifactLocation SARIFArtifactLocation `json:"artifactLocation"`
|
|
581
|
+
Region SARIFRegion `json:"region"`
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
type SARIFArtifactLocation struct {
|
|
585
|
+
URI string `json:"uri"`
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
type SARIFRegion struct {
|
|
589
|
+
StartLine int `json:"startLine"`
|
|
590
|
+
StartColumn int `json:"startColumn,omitempty"`
|
|
591
|
+
EndLine int `json:"endLine,omitempty"`
|
|
592
|
+
EndColumn int `json:"endColumn,omitempty"`
|
|
593
|
+
Snippet SARIFSnippet `json:"snippet,omitempty"`
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
type SARIFSnippet struct {
|
|
597
|
+
Text string `json:"text"`
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
type SARIFLogicalLocation struct {
|
|
601
|
+
Name string `json:"name,omitempty"`
|
|
602
|
+
Kind string `json:"kind,omitempty"` // function, module, etc.
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
type SARIFFix struct {
|
|
606
|
+
Description SARIFMessage `json:"description"`
|
|
607
|
+
ArtifactChanges []SARIFArtifactChange `json:"artifactChanges"`
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
type SARIFArtifactChange struct {
|
|
611
|
+
ArtifactLocation SARIFArtifactLocation `json:"artifactLocation"`
|
|
612
|
+
Replacements []SARIFReplacement `json:"replacements"`
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
type SARIFReplacement struct {
|
|
616
|
+
DeletedRegion SARIFRegion `json:"deletedRegion"`
|
|
617
|
+
InsertedContent SARIFContent `json:"insertedContent"`
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
type SARIFContent struct {
|
|
621
|
+
Text string `json:"text"`
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
type SARIFInvocation struct {
|
|
625
|
+
StartTimeUTC string `json:"startTimeUtc,omitempty"`
|
|
626
|
+
EndTimeUTC string `json:"endTimeUtc,omitempty"`
|
|
627
|
+
ExitStatus string `json:"exitStatus,omitempty"`
|
|
628
|
+
ExitStatusCode int `json:"exitStatusCode,omitempty"`
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## 5. Database Schema
|
|
635
|
+
|
|
636
|
+
### 5.1 Scans Table
|
|
637
|
+
|
|
638
|
+
```sql
|
|
639
|
+
CREATE TABLE scans (
|
|
640
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
641
|
+
scan_id VARCHAR(255) UNIQUE NOT NULL,
|
|
642
|
+
timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
643
|
+
duration_ms INTEGER NOT NULL,
|
|
644
|
+
config JSONB NOT NULL,
|
|
645
|
+
|
|
646
|
+
-- File counts
|
|
647
|
+
files_scanned INTEGER NOT NULL DEFAULT 0,
|
|
648
|
+
files_skipped INTEGER NOT NULL DEFAULT 0,
|
|
649
|
+
|
|
650
|
+
-- Issue summary
|
|
651
|
+
total_issues INTEGER NOT NULL DEFAULT 0,
|
|
652
|
+
critical_issues INTEGER NOT NULL DEFAULT 0,
|
|
653
|
+
high_issues INTEGER NOT NULL DEFAULT 0,
|
|
654
|
+
medium_issues INTEGER NOT NULL DEFAULT 0,
|
|
655
|
+
low_issues INTEGER NOT NULL DEFAULT 0,
|
|
656
|
+
|
|
657
|
+
-- Performance
|
|
658
|
+
performance JSONB,
|
|
659
|
+
|
|
660
|
+
-- AI usage
|
|
661
|
+
ai_enabled BOOLEAN NOT NULL DEFAULT false,
|
|
662
|
+
ai_usage JSONB,
|
|
663
|
+
|
|
664
|
+
-- Git context
|
|
665
|
+
git_branch VARCHAR(255),
|
|
666
|
+
git_commit VARCHAR(40),
|
|
667
|
+
|
|
668
|
+
-- Metadata
|
|
669
|
+
project_path VARCHAR(1024),
|
|
670
|
+
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
671
|
+
|
|
672
|
+
-- Indexes
|
|
673
|
+
INDEX idx_scans_timestamp (timestamp),
|
|
674
|
+
INDEX idx_scans_project (project_path)
|
|
675
|
+
);
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### 5.2 Issues Table
|
|
679
|
+
|
|
680
|
+
```sql
|
|
681
|
+
CREATE TABLE issues (
|
|
682
|
+
id BIGSERIAL PRIMARY KEY,
|
|
683
|
+
issue_id VARCHAR(255) UNIQUE NOT NULL,
|
|
684
|
+
scan_id VARCHAR(255) NOT NULL REFERENCES scans(scan_id),
|
|
685
|
+
|
|
686
|
+
-- Classification
|
|
687
|
+
rule_id VARCHAR(255) NOT NULL,
|
|
688
|
+
severity VARCHAR(50) NOT NULL,
|
|
689
|
+
category VARCHAR(50) NOT NULL,
|
|
690
|
+
confidence VARCHAR(50),
|
|
691
|
+
|
|
692
|
+
-- Location
|
|
693
|
+
file_path VARCHAR(1024) NOT NULL,
|
|
694
|
+
line_number INTEGER NOT NULL,
|
|
695
|
+
column_number INTEGER,
|
|
696
|
+
end_line INTEGER,
|
|
697
|
+
end_column INTEGER,
|
|
698
|
+
|
|
699
|
+
-- Description
|
|
700
|
+
title VARCHAR(512) NOT NULL,
|
|
701
|
+
description TEXT,
|
|
702
|
+
message TEXT,
|
|
703
|
+
|
|
704
|
+
-- Code context
|
|
705
|
+
code_snippet TEXT,
|
|
706
|
+
suggestion TEXT,
|
|
707
|
+
suggested_code TEXT,
|
|
708
|
+
fixable BOOLEAN DEFAULT false,
|
|
709
|
+
|
|
710
|
+
-- References
|
|
711
|
+
references JSONB,
|
|
712
|
+
cwe VARCHAR(20),
|
|
713
|
+
owasp VARCHAR(100),
|
|
714
|
+
|
|
715
|
+
-- AI
|
|
716
|
+
ai_enhanced BOOLEAN DEFAULT false,
|
|
717
|
+
ai_model VARCHAR(100),
|
|
718
|
+
|
|
719
|
+
-- Metadata
|
|
720
|
+
language VARCHAR(50),
|
|
721
|
+
extra JSONB,
|
|
722
|
+
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
723
|
+
|
|
724
|
+
-- Indexes
|
|
725
|
+
INDEX idx_issues_scan (scan_id),
|
|
726
|
+
INDEX idx_issues_file (file_path),
|
|
727
|
+
INDEX idx_issues_severity (severity),
|
|
728
|
+
INDEX idx_issues_rule (rule_id)
|
|
729
|
+
);
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
### 5.3 Rules Table
|
|
733
|
+
|
|
734
|
+
```sql
|
|
735
|
+
CREATE TABLE rules (
|
|
736
|
+
id SERIAL PRIMARY KEY,
|
|
737
|
+
rule_id VARCHAR(255) UNIQUE NOT NULL,
|
|
738
|
+
name VARCHAR(255) NOT NULL,
|
|
739
|
+
category VARCHAR(50) NOT NULL,
|
|
740
|
+
default_severity VARCHAR(50) NOT NULL,
|
|
741
|
+
|
|
742
|
+
-- Definition
|
|
743
|
+
definition JSONB NOT NULL,
|
|
744
|
+
languages VARCHAR(50) [],
|
|
745
|
+
|
|
746
|
+
-- Configuration
|
|
747
|
+
enabled BOOLEAN DEFAULT true,
|
|
748
|
+
custom BOOLEAN DEFAULT false,
|
|
749
|
+
|
|
750
|
+
-- Documentation
|
|
751
|
+
title VARCHAR(512),
|
|
752
|
+
description TEXT,
|
|
753
|
+
references JSONB,
|
|
754
|
+
|
|
755
|
+
-- Metadata
|
|
756
|
+
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
|
757
|
+
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
|
758
|
+
);
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### 5.4 Scan History View
|
|
762
|
+
|
|
763
|
+
```sql
|
|
764
|
+
CREATE VIEW scan_history AS
|
|
765
|
+
SELECT
|
|
766
|
+
date_trunc('day', timestamp) AS date,
|
|
767
|
+
COUNT(*) AS scan_count,
|
|
768
|
+
AVG(total_issues) AS avg_issues,
|
|
769
|
+
AVG(duration_ms) AS avg_duration_ms,
|
|
770
|
+
SUM(total_issues) AS total_issues,
|
|
771
|
+
AVG(critical_issues) AS avg_critical,
|
|
772
|
+
AVG(high_issues) AS avg_high,
|
|
773
|
+
AVG(medium_issues) AS avg_medium,
|
|
774
|
+
AVG(low_issues) AS avg_low
|
|
775
|
+
FROM scans
|
|
776
|
+
GROUP BY date_trunc('day', timestamp)
|
|
777
|
+
ORDER BY date DESC;
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
---
|
|
781
|
+
|
|
782
|
+
## 6. Cache Schema
|
|
783
|
+
|
|
784
|
+
### 6.1 Redis Cache Keys
|
|
785
|
+
|
|
786
|
+
``# AST Cache
|
|
787
|
+
ast:{language}:{file_hash} -> {
|
|
788
|
+
"ast": <serialized AST>,
|
|
789
|
+
"timestamp": <unix timestamp>,
|
|
790
|
+
"file_size": <bytes>
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
# Rule Cache
|
|
794
|
+
rules:{rule_id} -> {
|
|
795
|
+
"compiled": <compiled rule>,
|
|
796
|
+
"timestamp": <unix timestamp>
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
# Scan Session
|
|
800
|
+
session:{scan_id} -> {
|
|
801
|
+
"config": <scan config>,
|
|
802
|
+
"start_time": <unix timestamp>,
|
|
803
|
+
"files_scanned": <count>,
|
|
804
|
+
"issues_found": <count>
|
|
805
|
+
}
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
---
|
|
809
|
+
|
|
810
|
+
## 7. Next Steps
|
|
811
|
+
|
|
812
|
+
1. Review data model with stakeholders
|
|
813
|
+
2. Implement Go structs
|
|
814
|
+
3. Create database migration scripts
|
|
815
|
+
4. Implement JSON/YAML serialization
|
|
816
|
+
5. Create unit tests for data structures
|
|
817
|
+
6. Proceed to work breakdown (wbs.md)
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
## Metadata
|
|
822
|
+
|
|
823
|
+
- **Created**: 1/17/2026
|
|
824
|
+
- **Author**: Phase 3 Design Executor
|
|
825
|
+
- **Status**: Draft for Review
|
|
826
|
+
- **Related Docs**: phase3/architecture.md, phase3/api-design.md
|
|
827
|
+
|
|
828
|
+
---
|
|
829
|
+
|
|
830
|
+
*This data model design document defines the core data structures for the AI Code Review Tool.*
|