docguard-cli 0.9.10 → 0.10.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/README.md +3 -2
- package/cli/commands/diagnose.mjs +23 -15
- package/cli/commands/diff.mjs +110 -137
- package/cli/commands/fix.mjs +39 -3
- package/cli/commands/generate.mjs +57 -27
- package/cli/commands/guard.mjs +45 -24
- package/cli/commands/score.mjs +24 -2
- package/cli/docguard.mjs +0 -0
- package/cli/scanners/api-doc.mjs +122 -0
- package/cli/scanners/doc-tools.mjs +1 -1
- package/cli/scanners/routes.mjs +45 -32
- package/cli/shared-ignore.mjs +43 -0
- package/cli/shared-source.mjs +247 -0
- package/cli/validators/api-surface.mjs +179 -0
- package/cli/validators/architecture.mjs +4 -3
- package/cli/validators/changelog.mjs +42 -2
- package/cli/validators/doc-quality.mjs +3 -2
- package/cli/validators/docs-coverage.mjs +9 -14
- package/cli/validators/docs-diff.mjs +128 -85
- package/cli/validators/docs-sync.mjs +30 -24
- package/cli/validators/drift.mjs +6 -2
- package/cli/validators/environment.mjs +43 -3
- package/cli/validators/freshness.mjs +4 -3
- package/cli/validators/metadata-sync.mjs +11 -6
- package/cli/validators/metrics-consistency.mjs +4 -2
- package/cli/validators/schema-sync.mjs +19 -10
- package/cli/validators/security.mjs +20 -7
- package/cli/validators/structure.mjs +8 -1
- package/cli/validators/test-spec.mjs +26 -17
- package/cli/validators/todo-tracking.mjs +21 -8
- package/cli/validators/traceability.mjs +61 -36
- package/commands/docguard.guard.md +5 -4
- package/docs/quickstart.md +1 -1
- package/extensions/spec-kit-docguard/README.md +1 -1
- package/extensions/spec-kit-docguard/commands/guard.md +6 -5
- package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +3 -2
- package/package.json +1 -1
- package/templates/commands/docguard.guard.md +3 -3
|
@@ -58,6 +58,7 @@ export function validateSecurity(projectDir, config) {
|
|
|
58
58
|
const results = { name: 'security', errors: [], warnings: [], passed: 0, total: 0 };
|
|
59
59
|
|
|
60
60
|
const findings = [];
|
|
61
|
+
let scanned = 0;
|
|
61
62
|
|
|
62
63
|
walkDir(projectDir, (filePath) => {
|
|
63
64
|
const ext = extname(filePath);
|
|
@@ -73,13 +74,17 @@ export function validateSecurity(projectDir, config) {
|
|
|
73
74
|
// Apply config ignore patterns (securityIgnore + global ignore)
|
|
74
75
|
if (shouldIgnore(relPath, config, 'securityIgnore')) return;
|
|
75
76
|
|
|
77
|
+
scanned++;
|
|
76
78
|
const content = readFileSync(filePath, 'utf-8');
|
|
77
|
-
|
|
79
|
+
let lines = null;
|
|
78
80
|
|
|
79
81
|
for (const { pattern, label } of SECRET_PATTERNS) {
|
|
80
82
|
pattern.lastIndex = 0;
|
|
81
83
|
const match = pattern.exec(content);
|
|
82
84
|
if (match) {
|
|
85
|
+
// Lazily initialize lines only when a match is found
|
|
86
|
+
if (!lines) lines = content.split('\n');
|
|
87
|
+
|
|
83
88
|
// Find the line containing this match for context-aware filtering
|
|
84
89
|
const matchPos = match.index;
|
|
85
90
|
let charCount = 0;
|
|
@@ -100,13 +105,21 @@ export function validateSecurity(projectDir, config) {
|
|
|
100
105
|
}
|
|
101
106
|
});
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
results.
|
|
108
|
+
// Only count the secret scan as a passed check if we actually scanned files.
|
|
109
|
+
// An empty scan that reports "no secrets" is a dangerous false ✅ — surface it.
|
|
110
|
+
if (scanned > 0) {
|
|
111
|
+
results.total++;
|
|
112
|
+
if (findings.length === 0) {
|
|
113
|
+
results.passed++;
|
|
114
|
+
} else {
|
|
115
|
+
for (const f of findings) {
|
|
116
|
+
results.errors.push(`${f.file}: possible ${f.label} found`);
|
|
117
|
+
}
|
|
109
118
|
}
|
|
119
|
+
} else {
|
|
120
|
+
results.warnings.push(
|
|
121
|
+
'No source files were scanned for secrets — check config.sourceRoot / ignore patterns'
|
|
122
|
+
);
|
|
110
123
|
}
|
|
111
124
|
|
|
112
125
|
// Check .gitignore includes .env
|
|
@@ -76,7 +76,14 @@ export function validateDocSections(projectDir, config) {
|
|
|
76
76
|
|
|
77
77
|
for (const section of sections) {
|
|
78
78
|
results.total++;
|
|
79
|
-
|
|
79
|
+
// Match an actual heading at line start (any level), not a substring that
|
|
80
|
+
// could appear in a table-of-contents link or a code block.
|
|
81
|
+
const headingText = section.replace(/^#+\s*/, '');
|
|
82
|
+
const headingRe = new RegExp(
|
|
83
|
+
'^#{2,6}\\s+' + headingText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\b',
|
|
84
|
+
'm'
|
|
85
|
+
);
|
|
86
|
+
if (headingRe.test(content)) {
|
|
80
87
|
results.passed++;
|
|
81
88
|
} else {
|
|
82
89
|
results.warnings.push(`${file}: missing section "${section}"`);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
7
7
|
import { resolve } from 'node:path';
|
|
8
|
+
import { resolveSourceRoots } from '../shared-source.mjs';
|
|
8
9
|
|
|
9
10
|
export function validateTestSpec(projectDir, config) {
|
|
10
11
|
const results = { name: 'test-spec', errors: [], warnings: [], passed: 0, total: 0 };
|
|
@@ -43,6 +44,9 @@ export function validateTestSpec(projectDir, config) {
|
|
|
43
44
|
// Skip template/example rows and italic placeholder rows
|
|
44
45
|
if (sourceFile.startsWith('<!--') || sourceFile === 'Source File' || sourceFile.startsWith('*')) continue;
|
|
45
46
|
|
|
47
|
+
// Author-declared gaps (❌/⚠️) are surfaced as warnings. A ✅ glyph is the
|
|
48
|
+
// author's CLAIM, not proof — it is NOT counted as a pass. The real pass
|
|
49
|
+
// comes from the file-existence checks below (code truth, not the glyph).
|
|
46
50
|
if (status && status.includes('❌')) {
|
|
47
51
|
results.total++;
|
|
48
52
|
results.warnings.push(
|
|
@@ -53,9 +57,6 @@ export function validateTestSpec(projectDir, config) {
|
|
|
53
57
|
results.warnings.push(
|
|
54
58
|
`TEST-SPEC declares ${sourceFile} as ⚠️ — partial coverage`
|
|
55
59
|
);
|
|
56
|
-
} else if (status && status.includes('✅')) {
|
|
57
|
-
results.total++;
|
|
58
|
-
results.passed++;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
// ── File existence checks ───────────────────────────────────────
|
|
@@ -122,34 +123,41 @@ export function validateTestSpec(projectDir, config) {
|
|
|
122
123
|
results.warnings.push(
|
|
123
124
|
`E2E Journey #${num} (${journey}) — missing test: ${testFile}`
|
|
124
125
|
);
|
|
125
|
-
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// For a ✅ journey, verify the referenced test file actually exists
|
|
130
|
+
// rather than trusting the glyph.
|
|
131
|
+
const cleanTest = testFile ? testFile.replace(/`/g, '').trim() : '';
|
|
132
|
+
if (cleanTest && cleanTest !== '—' && !cleanTest.includes('N/A')) {
|
|
126
133
|
results.total++;
|
|
127
|
-
|
|
134
|
+
if (existsSync(resolve(projectDir, cleanTest))) {
|
|
135
|
+
results.passed++;
|
|
136
|
+
} else {
|
|
137
|
+
results.warnings.push(
|
|
138
|
+
`E2E Journey #${num} (${journey}) marked ✅ but test file not found: ${cleanTest}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
128
141
|
}
|
|
129
142
|
}
|
|
130
143
|
}
|
|
131
144
|
}
|
|
132
145
|
|
|
133
|
-
// If no test
|
|
146
|
+
// If TEST-SPEC.md declared no service-to-test mappings, there is nothing to
|
|
147
|
+
// verify against. Do NOT manufacture a 1/1 pass just because tests exist
|
|
148
|
+
// somewhere — that rendered a confident green ✅ for a doc that mapped nothing.
|
|
134
149
|
if (results.total === 0) {
|
|
135
|
-
results.total = 1;
|
|
136
|
-
|
|
137
150
|
// 1. Check top-level test dirs
|
|
138
151
|
const commonTestDirs = ['tests', 'test', '__tests__', 'spec'];
|
|
139
152
|
const hasTestDir = commonTestDirs.some(d =>
|
|
140
153
|
existsSync(resolve(projectDir, d))
|
|
141
154
|
);
|
|
142
155
|
|
|
143
|
-
// 2. Check co-located tests (
|
|
156
|
+
// 2. Check co-located tests (honors config.sourceRoot + workspaces)
|
|
144
157
|
let hasColocated = false;
|
|
145
158
|
if (!hasTestDir) {
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
const rootPath = resolve(projectDir, root);
|
|
149
|
-
if (existsSync(rootPath) && hasTestFilesRecursive(rootPath)) {
|
|
150
|
-
hasColocated = true;
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
159
|
+
for (const rootPath of resolveSourceRoots(projectDir, config)) {
|
|
160
|
+
if (hasTestFilesRecursive(rootPath)) { hasColocated = true; break; }
|
|
153
161
|
}
|
|
154
162
|
}
|
|
155
163
|
|
|
@@ -161,7 +169,8 @@ export function validateTestSpec(projectDir, config) {
|
|
|
161
169
|
}
|
|
162
170
|
|
|
163
171
|
if (hasTestDir || hasColocated || hasConfigTests) {
|
|
164
|
-
|
|
172
|
+
// Tests exist but the spec maps none of them → not applicable, not a pass.
|
|
173
|
+
results.note = 'TEST-SPEC.md declares no service-to-test mappings';
|
|
165
174
|
} else {
|
|
166
175
|
results.warnings.push(
|
|
167
176
|
'No test directory or co-located test files found. ' +
|
|
@@ -67,7 +67,7 @@ export function validateTodoTracking(projectDir, config) {
|
|
|
67
67
|
results.passed += skipResults.passed;
|
|
68
68
|
results.total += skipResults.total;
|
|
69
69
|
|
|
70
|
-
// ── Part 2: Untracked
|
|
70
|
+
// ── Part 2: Untracked Annotations ──
|
|
71
71
|
const todoResults = checkUntrackedTodos(projectDir, config);
|
|
72
72
|
results.errors.push(...todoResults.errors);
|
|
73
73
|
results.warnings.push(...todoResults.warnings);
|
|
@@ -105,6 +105,10 @@ function checkSkippedTests(projectDir, config) {
|
|
|
105
105
|
let content;
|
|
106
106
|
try { content = readFileSync(fullPath, 'utf-8'); } catch { continue; }
|
|
107
107
|
|
|
108
|
+
// Fast early-return: skip expensive string split if no skip patterns exist
|
|
109
|
+
const hasSkip = SKIP_PATTERNS.some(p => p.test(content));
|
|
110
|
+
if (!hasSkip) continue;
|
|
111
|
+
|
|
108
112
|
const lines = content.split('\n');
|
|
109
113
|
|
|
110
114
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -151,7 +155,7 @@ function checkSkippedTests(projectDir, config) {
|
|
|
151
155
|
return { errors, warnings, passed, total };
|
|
152
156
|
}
|
|
153
157
|
|
|
154
|
-
// ──── Untracked
|
|
158
|
+
// ──── Untracked Annotations ────────────────────────────────────────────────
|
|
155
159
|
|
|
156
160
|
/**
|
|
157
161
|
* Scan source files for TODO/FIXME annotations and check if they appear
|
|
@@ -183,15 +187,19 @@ function checkUntrackedTodos(projectDir, config) {
|
|
|
183
187
|
for (const todo of todos) {
|
|
184
188
|
// Check if the TODO is tracked in documentation
|
|
185
189
|
// Improved matching: check full text AND file location context
|
|
190
|
+
// ⚡ Bolt: Precompute string lowercasing and trimming once per TODO
|
|
191
|
+
// instead of inside the trackingContent.some loop
|
|
192
|
+
const todoTextLower = todo.text.toLowerCase().trim();
|
|
193
|
+
const searchText = todoTextLower.length > 20
|
|
194
|
+
? todoTextLower.substring(0, 40)
|
|
195
|
+
: todoTextLower;
|
|
196
|
+
|
|
186
197
|
const isTracked = trackingContent.some(doc => {
|
|
187
198
|
const content = doc.content;
|
|
188
|
-
|
|
189
|
-
const
|
|
199
|
+
// ⚡ Bolt: use the precomputed lowercased content
|
|
200
|
+
const contentLower = doc.contentLower;
|
|
190
201
|
|
|
191
202
|
// Match 1: Full TODO text appears in the doc (at least 20 chars or full text)
|
|
192
|
-
const searchText = todoTextLower.length > 20
|
|
193
|
-
? todoTextLower.substring(0, 40)
|
|
194
|
-
: todoTextLower;
|
|
195
203
|
const hasText = contentLower.includes(searchText);
|
|
196
204
|
|
|
197
205
|
// Match 2: File location appears nearby in the doc
|
|
@@ -240,7 +248,9 @@ function loadTrackingDocs(projectDir, config) {
|
|
|
240
248
|
const fullPath = resolve(projectDir, file);
|
|
241
249
|
if (existsSync(fullPath)) {
|
|
242
250
|
try {
|
|
243
|
-
|
|
251
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
252
|
+
// ⚡ Bolt: Precompute lowercased content during file load to avoid N*M overhead
|
|
253
|
+
docs.push({ file, content, contentLower: content.toLowerCase() });
|
|
244
254
|
} catch { /* ignore */ }
|
|
245
255
|
}
|
|
246
256
|
}
|
|
@@ -306,6 +316,9 @@ function findTodos(rootDir, dir, todos, config) {
|
|
|
306
316
|
let content;
|
|
307
317
|
try { content = readFileSync(full, 'utf-8'); } catch { continue; }
|
|
308
318
|
|
|
319
|
+
// Fast early-return: skip expensive string split if no TODO patterns exist
|
|
320
|
+
if (!TODO_PATTERN.test(content)) continue;
|
|
321
|
+
|
|
309
322
|
const lines = content.split('\n');
|
|
310
323
|
|
|
311
324
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -130,13 +130,16 @@ export function validateTraceability(projectDir, config) {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// Count matching source files
|
|
133
|
-
|
|
133
|
+
// ⚡ Bolt: Fast early return using .some() instead of .filter()
|
|
134
|
+
let hasSource = false;
|
|
134
135
|
for (const pattern of traceInfo.sourcePatterns) {
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
if (projectFiles.some(f => pattern.glob.test(f))) {
|
|
137
|
+
hasSource = true;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
137
140
|
}
|
|
138
141
|
|
|
139
|
-
if (
|
|
142
|
+
if (hasSource) {
|
|
140
143
|
passed++;
|
|
141
144
|
} else {
|
|
142
145
|
warnings.push(`${docName} — exists but no matching source code found (unlinked doc)`);
|
|
@@ -186,6 +189,46 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
186
189
|
: DEFAULT_REQ_PATTERNS;
|
|
187
190
|
|
|
188
191
|
// ── Step 1: Collect requirement IDs from documentation ──
|
|
192
|
+
const reqIds = collectRequirementIds(projectDir, config, patterns);
|
|
193
|
+
|
|
194
|
+
// If no requirement IDs found, silently pass — this project doesn't use them
|
|
195
|
+
if (reqIds.size === 0) {
|
|
196
|
+
return { errors, warnings, passed, total };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ── Step 2: Scan test files for requirement ID references ──
|
|
200
|
+
const testRefs = scanTestFilesForReferences(projectDir, projectFiles, patterns);
|
|
201
|
+
|
|
202
|
+
// ── Step 3: Report traceability results ──
|
|
203
|
+
|
|
204
|
+
// Check each documented requirement has at least one test reference
|
|
205
|
+
for (const [reqId, location] of reqIds) {
|
|
206
|
+
total++;
|
|
207
|
+
if (testRefs.has(reqId)) {
|
|
208
|
+
passed++;
|
|
209
|
+
} else {
|
|
210
|
+
warnings.push(
|
|
211
|
+
`Requirement ${reqId} (${location.file}:${location.line}) has no test coverage. ` +
|
|
212
|
+
`Add @req ${reqId} comment to the test that verifies this requirement`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Check for orphaned test refs (tests referencing non-existent requirements)
|
|
218
|
+
for (const [reqId, refs] of testRefs) {
|
|
219
|
+
if (!reqIds.has(reqId)) {
|
|
220
|
+
total++;
|
|
221
|
+
warnings.push(
|
|
222
|
+
`Test references ${reqId} (${refs[0].file}:${refs[0].line}) but no requirement ` +
|
|
223
|
+
`with this ID exists in documentation. Remove the reference or add the requirement to docs`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { errors, warnings, passed, total };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function collectRequirementIds(projectDir, config, patterns) {
|
|
189
232
|
const reqIds = new Map(); // reqId → { file, line }
|
|
190
233
|
const docSearchPaths = getRequirementDocPaths(projectDir, config);
|
|
191
234
|
|
|
@@ -193,6 +236,11 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
193
236
|
if (!existsSync(docPath)) continue;
|
|
194
237
|
|
|
195
238
|
const content = readFileSync(docPath, 'utf-8');
|
|
239
|
+
|
|
240
|
+
// Fast early-return: skip expensive string split if no requirement patterns exist
|
|
241
|
+
const hasMatch = patterns.some(p => { p.lastIndex = 0; return p.test(content); });
|
|
242
|
+
if (!hasMatch) continue;
|
|
243
|
+
|
|
196
244
|
const lines = content.split('\n');
|
|
197
245
|
const docName = relative(projectDir, docPath);
|
|
198
246
|
|
|
@@ -211,12 +259,10 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
211
259
|
}
|
|
212
260
|
}
|
|
213
261
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return { errors, warnings, passed, total };
|
|
217
|
-
}
|
|
262
|
+
return reqIds;
|
|
263
|
+
}
|
|
218
264
|
|
|
219
|
-
|
|
265
|
+
function scanTestFilesForReferences(projectDir, projectFiles, patterns) {
|
|
220
266
|
const testFiles = projectFiles.filter(f =>
|
|
221
267
|
/\.(test|spec)\.(mjs|cjs|[jt]sx?)$/.test(f) ||
|
|
222
268
|
/__tests__\//.test(f) ||
|
|
@@ -231,6 +277,11 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
231
277
|
|
|
232
278
|
let content;
|
|
233
279
|
try { content = readFileSync(fullPath, 'utf-8'); } catch { continue; }
|
|
280
|
+
|
|
281
|
+
// Fast early-return: skip expensive string split if no requirement patterns exist
|
|
282
|
+
const hasMatch = patterns.some(p => { p.lastIndex = 0; return p.test(content); });
|
|
283
|
+
if (!hasMatch) continue;
|
|
284
|
+
|
|
234
285
|
const lines = content.split('\n');
|
|
235
286
|
|
|
236
287
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -246,33 +297,7 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
246
297
|
}
|
|
247
298
|
}
|
|
248
299
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// Check each documented requirement has at least one test reference
|
|
252
|
-
for (const [reqId, location] of reqIds) {
|
|
253
|
-
total++;
|
|
254
|
-
if (testRefs.has(reqId)) {
|
|
255
|
-
passed++;
|
|
256
|
-
} else {
|
|
257
|
-
warnings.push(
|
|
258
|
-
`Requirement ${reqId} (${location.file}:${location.line}) has no test coverage. ` +
|
|
259
|
-
`Add @req ${reqId} comment to the test that verifies this requirement`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Check for orphaned test refs (tests referencing non-existent requirements)
|
|
265
|
-
for (const [reqId, refs] of testRefs) {
|
|
266
|
-
if (!reqIds.has(reqId)) {
|
|
267
|
-
total++;
|
|
268
|
-
warnings.push(
|
|
269
|
-
`Test references ${reqId} (${refs[0].file}:${refs[0].line}) but no requirement ` +
|
|
270
|
-
`with this ID exists in documentation. Remove the reference or add the requirement to docs`
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return { errors, warnings, passed, total };
|
|
300
|
+
return testRefs;
|
|
276
301
|
}
|
|
277
302
|
|
|
278
303
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Run DocGuard guard validation — check project documentation against CDD standards with
|
|
2
|
+
description: Run DocGuard guard validation — check project documentation against CDD standards with 20 validators
|
|
3
3
|
handoffs:
|
|
4
4
|
- label: Fix All Issues
|
|
5
5
|
agent: docguard.fix
|
|
@@ -23,14 +23,14 @@ Run the DocGuard CLI to validate all documentation against Canonical-Driven Deve
|
|
|
23
23
|
npx docguard-cli guard
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
2. **Parse the output**. Each of the
|
|
26
|
+
2. **Parse the output**. Each of the 20 validators reports ✅ (pass), ⚠️ (warning), ❌ (fail), or ➖ (N/A — nothing to validate). **A ➖ N/A is NOT a pass**: it means the validator found nothing to check (e.g. no API-REFERENCE.md, no DB schema, no layer boundaries declared). Don't read N/A as "healthy" — read it as "not assessed".
|
|
27
27
|
|
|
28
28
|
| Validator | What It Checks |
|
|
29
29
|
|-----------|---------------|
|
|
30
30
|
| Structure | Required CDD files exist |
|
|
31
31
|
| Doc Sections | Canonical docs have required sections |
|
|
32
32
|
| Docs-Sync | External doc references are valid |
|
|
33
|
-
| Drift |
|
|
33
|
+
| Drift-Comments | `// DRIFT:` code comments logged in DRIFT-LOG.md |
|
|
34
34
|
| Changelog | CHANGELOG.md is maintained |
|
|
35
35
|
| Test-Spec | Tests match TEST-SPEC.md rules |
|
|
36
36
|
| Environment | Environment documentation |
|
|
@@ -39,6 +39,7 @@ npx docguard-cli guard
|
|
|
39
39
|
| Freshness | Docs updated within commit window |
|
|
40
40
|
| Traceability | Requirements trace to tests |
|
|
41
41
|
| Docs-Diff | Doc changes match code changes |
|
|
42
|
+
| API-Surface | API-REFERENCE.md endpoints match the real API surface (OpenAPI spec / routes) |
|
|
42
43
|
| Metadata-Sync | Metadata headers are consistent |
|
|
43
44
|
| Docs-Coverage | All config files documented |
|
|
44
45
|
| Doc-Quality | Readability, IEEE 830 compliance |
|
|
@@ -49,7 +50,7 @@ npx docguard-cli guard
|
|
|
49
50
|
|
|
50
51
|
3. **Triage findings by severity**:
|
|
51
52
|
- **CRITICAL**: Structure, Security, Test-Spec failures
|
|
52
|
-
- **HIGH**: Doc Sections, Drift, Changelog, Traceability failures
|
|
53
|
+
- **HIGH**: Doc Sections, Drift-Comments, Changelog, Traceability, API-Surface (documented-but-absent endpoint) failures
|
|
53
54
|
- **MEDIUM**: Freshness, Docs-Coverage, Doc-Quality, Metrics-Consistency warnings
|
|
54
55
|
- **LOW**: TODO-Tracking, Schema-Sync, Spec-Kit, Metadata-Sync warnings
|
|
55
56
|
|
package/docs/quickstart.md
CHANGED
|
@@ -68,7 +68,7 @@ diagnose → AI reads prompts → AI fixes docs → guard verifies
|
|
|
68
68
|
## Verify
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
npx docguard-cli guard # Pass/fail check (
|
|
71
|
+
npx docguard-cli guard # Pass/fail check (20 validators)
|
|
72
72
|
npx docguard-cli score # 0-100 maturity score
|
|
73
73
|
```
|
|
74
74
|
|
|
@@ -4,7 +4,7 @@ Enterprise-grade Canonical-Driven Development (CDD) enforcement for [Spec Kit](h
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
7
|
+
- **20 Validators** — Structure, Security, Doc Quality, Test-Spec, Drift-Comments, API-Surface, Freshness, and 13 more
|
|
8
8
|
- **4 AI Skills** — Enterprise-grade AI behavior protocols (not just step-lists)
|
|
9
9
|
- **3 Bash Scripts** — JSON-output orchestration for AI consumption
|
|
10
10
|
- **Workflow Chaining** — YAML handoffs enable guard → fix → review → score flows
|
|
@@ -14,7 +14,7 @@ handoffs:
|
|
|
14
14
|
|
|
15
15
|
# DocGuard Guard
|
|
16
16
|
|
|
17
|
-
Validate your project against its canonical documentation. Runs 160+ automated checks across
|
|
17
|
+
Validate your project against its canonical documentation. Runs 160+ automated checks across 20 validators.
|
|
18
18
|
|
|
19
19
|
## User Input
|
|
20
20
|
|
|
@@ -31,11 +31,11 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|
|
31
31
|
npx --yes docguard-cli@latest guard $ARGUMENTS
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
2. Parse each validator's result and build a severity-ranked findings table.
|
|
34
|
+
2. Parse each validator's result and build a severity-ranked findings table. Status glyphs: ✅ pass, ⚠️ warning, ❌ fail, ➖ N/A (nothing to validate — NOT a pass; the dimension was not assessed).
|
|
35
35
|
|
|
36
36
|
3. **Triage by severity**:
|
|
37
37
|
- **CRITICAL**: Structure, Security, Test-Spec failures → fix immediately
|
|
38
|
-
- **HIGH**: Doc Sections, Drift, Changelog, Traceability → fix before commit
|
|
38
|
+
- **HIGH**: Doc Sections, Drift-Comments, Changelog, Traceability, API-Surface → fix before commit
|
|
39
39
|
- **MEDIUM**: Freshness, Docs-Coverage, Doc-Quality, Metrics → fix this sprint
|
|
40
40
|
- **LOW**: TODO-Tracking, Schema-Sync, Spec-Kit, Metadata → fix when convenient
|
|
41
41
|
|
|
@@ -43,14 +43,14 @@ npx --yes docguard-cli@latest guard $ARGUMENTS
|
|
|
43
43
|
|
|
44
44
|
5. Re-run guard after fixes. Iterate until all checks pass (max 3 iterations).
|
|
45
45
|
|
|
46
|
-
## Validators (
|
|
46
|
+
## Validators (20 total)
|
|
47
47
|
|
|
48
48
|
| Validator | What It Checks |
|
|
49
49
|
|-----------|---------------|
|
|
50
50
|
| Structure | Required CDD files exist |
|
|
51
51
|
| Doc Sections | Canonical docs have required sections |
|
|
52
52
|
| Docs-Sync | External doc references are valid |
|
|
53
|
-
| Drift | `// DRIFT:` comments have DRIFT-LOG entries |
|
|
53
|
+
| Drift-Comments | `// DRIFT:` comments have DRIFT-LOG entries |
|
|
54
54
|
| Changelog | CHANGELOG.md is maintained |
|
|
55
55
|
| Test-Spec | TEST-SPEC.md matches actual test files |
|
|
56
56
|
| Environment | Environment docs and .env.example exist |
|
|
@@ -59,6 +59,7 @@ npx --yes docguard-cli@latest guard $ARGUMENTS
|
|
|
59
59
|
| Freshness | Docs updated after recent code changes |
|
|
60
60
|
| Traceability | Canonical docs linked to source code |
|
|
61
61
|
| Docs-Diff | Entity/route/field drift between code and docs |
|
|
62
|
+
| API-Surface | API-REFERENCE.md endpoints match the real API surface (OpenAPI spec / routes) |
|
|
62
63
|
| Metadata-Sync | Metadata headers are consistent |
|
|
63
64
|
| Docs-Coverage | All config files documented |
|
|
64
65
|
| Doc-Quality | Readability, IEEE 830 compliance |
|
|
@@ -78,7 +78,8 @@ Classify every non-passing check using this priority matrix:
|
|
|
78
78
|
|
|
79
79
|
**HIGH (fix before commit)**:
|
|
80
80
|
- Doc Sections failures (missing required sections)
|
|
81
|
-
- Drift
|
|
81
|
+
- Drift-Comments (a `// DRIFT:` comment without a DRIFT-LOG.md entry)
|
|
82
|
+
- API-Surface (API-REFERENCE.md documents an endpoint that no longer exists in code)
|
|
82
83
|
- Changelog gaps
|
|
83
84
|
- Traceability breaks
|
|
84
85
|
|
|
@@ -138,7 +139,7 @@ For each finding, provide a **specific, actionable fix** — not "fix the issue"
|
|
|
138
139
|
|
|
139
140
|
Based on the triage results:
|
|
140
141
|
|
|
141
|
-
- **If all PASS**: "All
|
|
142
|
+
- **If all PASS**: "All 20 validators passed. Project is CDD-compliant. Ready to commit."
|
|
142
143
|
- **If only MEDIUM/LOW warnings**: "Non-blocking warnings found. Safe to commit, but consider running `/docguard.fix` for automated remediation."
|
|
143
144
|
- **If HIGH or CRITICAL failures**: "Blocking issues found. Fix these before committing. Suggest running `/docguard.fix --doc [most impactful doc]` next."
|
|
144
145
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Run DocGuard guard validation — check all
|
|
2
|
+
description: Run DocGuard guard validation — check all 20 validators and fix any issues
|
|
3
3
|
handoffs:
|
|
4
4
|
- label: Fix Issues
|
|
5
5
|
agent: docguard.fix
|
|
@@ -19,12 +19,12 @@ You are an AI agent enforcing Canonical-Driven Development (CDD) compliance usin
|
|
|
19
19
|
npx docguard-cli guard
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
Read the output. It shows pass (✅), warn (⚠️), or fail (❌) for each of the
|
|
22
|
+
Read the output. It shows pass (✅), warn (⚠️), or fail (❌) for each of the 20 validators:
|
|
23
23
|
|
|
24
24
|
| Priority | Validators |
|
|
25
25
|
|----------|-----------|
|
|
26
26
|
| CRITICAL | Structure, Security, Test-Spec |
|
|
27
|
-
| HIGH | Doc Sections, Drift, Changelog, Traceability |
|
|
27
|
+
| HIGH | Doc Sections, Drift-Comments, Changelog, Traceability, API-Surface |
|
|
28
28
|
| MEDIUM | Freshness, Docs-Coverage, Doc-Quality, Metrics-Consistency |
|
|
29
29
|
| LOW | TODO-Tracking, Schema-Sync, Spec-Kit, Metadata-Sync |
|
|
30
30
|
|