docguard-cli 0.40.5 → 0.41.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/CHANGELOG.md +3218 -0
- package/README.md +25 -16
- package/cli/assessment.mjs +94 -0
- package/cli/commands/ci.mjs +15 -5
- package/cli/commands/diagnose.mjs +20 -13
- package/cli/commands/fix.mjs +14 -45
- package/cli/commands/guard.mjs +53 -25
- package/cli/commands/hooks.mjs +51 -10
- package/cli/commands/init.mjs +15 -0
- package/cli/commands/reconcile.mjs +10 -3
- package/cli/commands/report.mjs +5 -1
- package/cli/commands/score.mjs +2 -1
- package/cli/commands/upgrade.mjs +4 -1
- package/cli/commands/verify.mjs +9 -2
- package/cli/commands/watch.mjs +3 -2
- package/cli/config.mjs +23 -0
- package/cli/evidence/adapters.mjs +14 -0
- package/cli/evidence/manifest.mjs +15 -0
- package/cli/evidence/python-literal.mjs +304 -0
- package/cli/findings.mjs +17 -3
- package/cli/scanners/instruction-audit.mjs +88 -11
- package/cli/scanners/js-ast.mjs +156 -18
- package/cli/scanners/reconciliation.mjs +56 -6
- package/cli/scanners/routes.mjs +84 -9
- package/cli/scanners/spec-registry.mjs +29 -0
- package/cli/shared-git.mjs +98 -0
- package/cli/shared-ignore.mjs +1 -1
- package/cli/shared.mjs +30 -1
- package/cli/validators/api-doc-smells.mjs +2 -2
- package/cli/validators/api-surface.mjs +4 -9
- package/cli/validators/diff-suspicion.mjs +3 -2
- package/cli/validators/docs-sync.mjs +45 -29
- package/cli/validators/environment.mjs +64 -6
- package/cli/validators/metrics-consistency.mjs +52 -11
- package/cli/validators/reference-existence.mjs +4 -2
- package/cli/validators/security.mjs +37 -12
- package/cli/validators/spec-registry.mjs +10 -7
- package/cli/validators/todo-tracking.mjs +31 -11
- package/cli/validators/traceability.mjs +29 -4
- package/cli/writers/junit.mjs +3 -3
- package/cli/writers/sarif.mjs +13 -9
- package/docs/configuration.md +12 -1
- package/extensions/spec-kit-docguard/extension.yml +1 -1
- package/extensions/spec-kit-docguard/skills/docguard-fix/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-review/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-score/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-sync/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/templates/github-workflows/docguard-autofix.yml +1 -1
- package/extensions/spec-kit-docguard/templates/github-workflows/docguard-guard.yml +1 -1
- package/package.json +2 -1
- package/schemas/docguard-config.schema.json +15 -1
- package/schemas/docguard-evidence.schema.json +12 -0
- package/templates/ci/github-actions.yml +1 -1
- package/templates/evidence-manifest.json +16 -0
|
@@ -18,6 +18,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
|
18
18
|
import { resolve, join, relative, basename, extname } from 'node:path';
|
|
19
19
|
import { TRACE_MAP, isTraceableSource } from '../shared-trace-patterns.mjs';
|
|
20
20
|
import { walkFiles as sharedWalkFiles, listCanonicalDocs } from '../shared-ignore.mjs';
|
|
21
|
+
/** @implements docguard.adoption-workflow-integrity#FR-006 */
|
|
21
22
|
import { mkFinding, resultFromFindings } from '../findings.mjs';
|
|
22
23
|
import { tokenize } from '../shared-diff.mjs';
|
|
23
24
|
import { rankBySimilarity } from '../shared-ir.mjs';
|
|
@@ -32,7 +33,7 @@ import {
|
|
|
32
33
|
requirementPatterns,
|
|
33
34
|
} from '../shared-requirements.mjs';
|
|
34
35
|
import { readRetirementManifest } from '../scanners/retirement-manifest.mjs';
|
|
35
|
-
import { parseSpecId } from '../scanners/spec-registry.mjs';
|
|
36
|
+
import { parseSpecId, trustedSpecLifecycleIndex } from '../scanners/spec-registry.mjs';
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Optional graphify interop (github.com/Graphify-Labs/graphify, MIT).
|
|
@@ -250,7 +251,9 @@ export function validateTraceability(projectDir, config) {
|
|
|
250
251
|
passed += reqResult.passed;
|
|
251
252
|
total += reqResult.total;
|
|
252
253
|
|
|
253
|
-
|
|
254
|
+
const result = resultFromFindings(findings, { passed, total });
|
|
255
|
+
result.requirementCoverage = reqResult.requirementCoverage;
|
|
256
|
+
return result;
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
// ──── Requirement ID Traceability ────────────────────────────────────────────
|
|
@@ -274,6 +277,7 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
274
277
|
// ── Step 1: Collect requirement IDs from documentation ──
|
|
275
278
|
const reqIds = collectRequirementIds(projectDir, config, patterns);
|
|
276
279
|
const retiredReqIds = loadRetiredRequirementIds(projectDir);
|
|
280
|
+
const lifecycleIndex = trustedSpecLifecycleIndex(projectDir);
|
|
277
281
|
|
|
278
282
|
// ── Step 2: Scan test files for requirement ID references ──
|
|
279
283
|
const testRefs = scanTestFilesForReferences(projectDir, projectFiles, patterns);
|
|
@@ -295,9 +299,18 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
295
299
|
const softThreshold = config.traceability?.irSoftThreshold ?? 0.10;
|
|
296
300
|
let testCorpus = null;
|
|
297
301
|
|
|
298
|
-
|
|
302
|
+
let deferred = 0;
|
|
303
|
+
let lifecycleUnknown = 0;
|
|
304
|
+
// Planned specs are intent awaiting implementation. Only committed,
|
|
305
|
+
// digest-current reviewed lifecycle can defer their test linkage.
|
|
299
306
|
for (const [key, location] of reqIds) {
|
|
300
307
|
const reqId = location.id;
|
|
308
|
+
const lifecycle = location.specId ? lifecycleIndex.get(`${location.specId}\0${location.file}`) : null;
|
|
309
|
+
if (lifecycle?.delivery === 'planned') {
|
|
310
|
+
deferred++;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (location.specId && !lifecycle) lifecycleUnknown++;
|
|
301
314
|
total++;
|
|
302
315
|
if (resolvedRefs.has(key)) {
|
|
303
316
|
passed++;
|
|
@@ -326,6 +339,8 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
326
339
|
}));
|
|
327
340
|
}
|
|
328
341
|
}
|
|
342
|
+
const applicableRequirements = total;
|
|
343
|
+
const tracedRequirements = passed;
|
|
329
344
|
|
|
330
345
|
// Check for orphaned test refs (tests referencing non-existent requirements)
|
|
331
346
|
for (const [reqId, refs] of testRefs) {
|
|
@@ -346,7 +361,17 @@ function validateRequirementTraceability(projectDir, config, projectFiles) {
|
|
|
346
361
|
}
|
|
347
362
|
}
|
|
348
363
|
|
|
349
|
-
return {
|
|
364
|
+
return {
|
|
365
|
+
findings, passed, total,
|
|
366
|
+
requirementCoverage: {
|
|
367
|
+
discovered: reqIds.size,
|
|
368
|
+
applicable: applicableRequirements,
|
|
369
|
+
traced: tracedRequirements,
|
|
370
|
+
missing: findings.filter(finding => finding.code === 'TRC004').length,
|
|
371
|
+
deferred,
|
|
372
|
+
lifecycleUnknown,
|
|
373
|
+
},
|
|
374
|
+
};
|
|
350
375
|
}
|
|
351
376
|
|
|
352
377
|
/**
|
package/cli/writers/junit.mjs
CHANGED
|
@@ -48,8 +48,8 @@ export function toJUnit(data) {
|
|
|
48
48
|
const vFindings = Array.isArray(v.findings)
|
|
49
49
|
? v.findings
|
|
50
50
|
: (data.findings || []).filter(f => f.validator === v.key || f.validator === v.name);
|
|
51
|
-
const errors = vFindings.filter(f => f.severity === 'error');
|
|
52
|
-
const warns = vFindings.filter(f => f.severity !== 'error');
|
|
51
|
+
const errors = vFindings.filter(f => (f.effectiveSeverity || f.severity) === 'error');
|
|
52
|
+
const warns = vFindings.filter(f => (f.effectiveSeverity || f.severity) !== 'error');
|
|
53
53
|
const attrs = `name="${esc(v.name)}" classname="docguard.guard"`;
|
|
54
54
|
|
|
55
55
|
if (v.status === 'skipped' || v.status === 'na') {
|
|
@@ -63,7 +63,7 @@ export function toJUnit(data) {
|
|
|
63
63
|
` <failure message="${esc(errors[0].message)}" type="${esc(errors[0].code || 'docguard')}">${esc(body)}</failure>\n` +
|
|
64
64
|
` </testcase>`
|
|
65
65
|
);
|
|
66
|
-
} else if (v.status === 'fail') {
|
|
66
|
+
} else if (v.status === 'fail' && vFindings.length === 0) {
|
|
67
67
|
// A validator that failed WITHOUT structured error findings — the
|
|
68
68
|
// crash path (guard catches the throw and records string errors only).
|
|
69
69
|
// This must go red in CI, not render as a passing testcase (M1).
|
package/cli/writers/sarif.mjs
CHANGED
|
@@ -25,9 +25,9 @@ function pkgInfo() {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/** severity
|
|
28
|
+
/** Effective severity → SARIF level. */
|
|
29
29
|
function toLevel(severity) {
|
|
30
|
-
return severity === 'error' ? 'error' : 'warning';
|
|
30
|
+
return severity === 'error' ? 'error' : severity === 'info' ? 'note' : 'warning';
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -62,10 +62,11 @@ export function toSarif(guardData, opts = {}) {
|
|
|
62
62
|
if (v.status === 'skipped' || v.status === 'na') continue;
|
|
63
63
|
if (Array.isArray(v.findings) && v.findings.length > 0) continue;
|
|
64
64
|
for (const msg of v.errors || []) {
|
|
65
|
-
synthetic.push({ code: `DOCGUARD-${String(v.key || v.name || 'unknown').toUpperCase()}`, severity: 'error', message: msg, location: null, suggestion: null });
|
|
65
|
+
synthetic.push({ code: `DOCGUARD-${String(v.key || v.name || 'unknown').toUpperCase()}`, severity: 'error', effectiveSeverity: 'error', message: msg, location: null, suggestion: null });
|
|
66
66
|
}
|
|
67
67
|
for (const msg of v.warnings || []) {
|
|
68
|
-
|
|
68
|
+
const effectiveSeverity = v.severity === 'high' ? 'error' : v.severity === 'low' ? 'info' : 'warn';
|
|
69
|
+
synthetic.push({ code: `DOCGUARD-${String(v.key || v.name || 'unknown').toUpperCase()}`, severity: 'warn', effectiveSeverity, message: msg, location: null, suggestion: null });
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
const all = [...findings, ...synthetic];
|
|
@@ -83,7 +84,7 @@ export function toSarif(guardData, opts = {}) {
|
|
|
83
84
|
rule.fullDescription = { text: meta.help };
|
|
84
85
|
}
|
|
85
86
|
rule.helpUri = HELP_URI;
|
|
86
|
-
rule.defaultConfiguration = { level: toLevel(f.severity) };
|
|
87
|
+
rule.defaultConfiguration = { level: toLevel(f.effectiveSeverity || f.severity) };
|
|
87
88
|
ruleIndexByCode.set(f.code, rules.length);
|
|
88
89
|
rules.push(rule);
|
|
89
90
|
}
|
|
@@ -92,7 +93,7 @@ export function toSarif(guardData, opts = {}) {
|
|
|
92
93
|
const result = {
|
|
93
94
|
ruleId: f.code,
|
|
94
95
|
ruleIndex: ruleIndexByCode.get(f.code),
|
|
95
|
-
level: toLevel(f.severity),
|
|
96
|
+
level: toLevel(f.effectiveSeverity || f.severity),
|
|
96
97
|
message: { text: f.suggestion && f.suggestion.text ? `${f.message}\n→ ${f.suggestion.text}` : f.message },
|
|
97
98
|
};
|
|
98
99
|
const loc = parseLocation(f.location);
|
|
@@ -101,9 +102,12 @@ export function toSarif(guardData, opts = {}) {
|
|
|
101
102
|
if (loc.line) physicalLocation.region = { startLine: loc.line };
|
|
102
103
|
result.locations = [{ physicalLocation }];
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
result.properties = {
|
|
106
|
+
originalSeverity: f.severity,
|
|
107
|
+
effectiveSeverity: f.effectiveSeverity || f.severity,
|
|
108
|
+
...(f.enforcement ? { enforcement: f.enforcement } : {}),
|
|
109
|
+
...(f.confidence === 'low' ? { confidence: 'low', reportable: !!f.reportable } : {}),
|
|
110
|
+
};
|
|
107
111
|
return result;
|
|
108
112
|
});
|
|
109
113
|
|
package/docs/configuration.md
CHANGED
|
@@ -7,7 +7,7 @@ DocGuard is configured via `.docguard.json` in the project root. If no config fi
|
|
|
7
7
|
```json
|
|
8
8
|
{
|
|
9
9
|
"projectName": "my-project",
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.6",
|
|
11
11
|
"profile": "standard",
|
|
12
12
|
"projectType": "webapp",
|
|
13
13
|
|
|
@@ -57,6 +57,10 @@ DocGuard is configured via `.docguard.json` in the project root. If no config fi
|
|
|
57
57
|
|
|
58
58
|
"docs": {
|
|
59
59
|
"dirs": ["reference", "website/docs"]
|
|
60
|
+
},
|
|
61
|
+
"findingSeverity": {
|
|
62
|
+
"TRC004": "low",
|
|
63
|
+
"SEC001": "high"
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
```
|
|
@@ -94,6 +98,13 @@ anything from display: `"high"` promotes its warnings to blocking (CI fails),
|
|
|
94
98
|
`"low"` demotes them (shown, but never fail the build). Valid values:
|
|
95
99
|
`high | medium | low`. To silence a validator entirely, use `validators.<key>: false`.
|
|
96
100
|
|
|
101
|
+
`findingSeverity.<CODE>` applies the same enforcement levels to one stable
|
|
102
|
+
finding code and takes precedence over the validator setting. This is the
|
|
103
|
+
preferred control for a noisy rule because neighboring findings retain their
|
|
104
|
+
policy. Intrinsic errors remain blocking unless their exact code is configured.
|
|
105
|
+
Machine outputs preserve `severity` and add `effectiveSeverity` plus the
|
|
106
|
+
enforcement source so audit consumers can distinguish detection from policy.
|
|
107
|
+
|
|
97
108
|
## Collections — verify documented counts against code
|
|
98
109
|
|
|
99
110
|
`collections` binds a documentation noun to a glob whose **file count is the
|
|
@@ -3,7 +3,7 @@ schema_version: "1.0"
|
|
|
3
3
|
extension:
|
|
4
4
|
id: "docguard"
|
|
5
5
|
name: "DocGuard — CDD Enforcement"
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.41.0"
|
|
7
7
|
description: "Documentation integrity for AI-assisted repositories: lifecycle registry, drift validation, traceability, safe archival, SARIF/JUnit, MCP, GitHub Actions, and Spec Kit hooks."
|
|
8
8
|
author: "Ricardo Accioly"
|
|
9
9
|
repository: "https://github.com/raccioly/docguard"
|
|
@@ -6,10 +6,10 @@ description: AI-driven documentation repair with structured research workflow, t
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.
|
|
9
|
+
version: 0.41.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-fix
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.41.0 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Fix Skill
|
|
15
15
|
|
|
@@ -7,10 +7,10 @@ description: Run DocGuard guard validation against Canonical-Driven Development
|
|
|
7
7
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
8
8
|
metadata:
|
|
9
9
|
author: docguard
|
|
10
|
-
version: 0.
|
|
10
|
+
version: 0.41.0
|
|
11
11
|
source: extensions/spec-kit-docguard/skills/docguard-guard
|
|
12
12
|
---
|
|
13
|
-
<!-- docguard:version: 0.
|
|
13
|
+
<!-- docguard:version: 0.41.0 -->
|
|
14
14
|
|
|
15
15
|
# DocGuard Guard Skill
|
|
16
16
|
|
|
@@ -6,10 +6,10 @@ description: Cross-document consistency analysis and quality assessment. Perform
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.
|
|
9
|
+
version: 0.41.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-review
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.41.0 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Review Skill
|
|
15
15
|
|
|
@@ -6,10 +6,10 @@ description: CDD maturity assessment with category-aware improvement roadmap. Ru
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.
|
|
9
|
+
version: 0.41.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-score
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.41.0 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Score Skill
|
|
15
15
|
|
|
@@ -4,10 +4,10 @@ description: Keep canonical documentation ALWAYS UP TO DATE. Refreshes code-trut
|
|
|
4
4
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
5
5
|
metadata:
|
|
6
6
|
author: docguard
|
|
7
|
-
version: 0.
|
|
7
|
+
version: 0.41.0
|
|
8
8
|
source: extensions/spec-kit-docguard/skills/docguard-sync
|
|
9
9
|
---
|
|
10
|
-
<!-- docguard:version: 0.
|
|
10
|
+
<!-- docguard:version: 0.41.0 -->
|
|
11
11
|
|
|
12
12
|
# DocGuard Sync Skill
|
|
13
13
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docguard-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "The enforcement tool for Canonical-Driven Development (CDD). Audit, generate, and guard your project documentation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"STANDARD.md",
|
|
62
62
|
"PRIVACY.md",
|
|
63
63
|
"PHILOSOPHY.md",
|
|
64
|
+
"CHANGELOG.md",
|
|
64
65
|
"README.md",
|
|
65
66
|
"LICENSE"
|
|
66
67
|
]
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"version": {
|
|
13
13
|
"type": "string",
|
|
14
|
-
"description": "Schema version (0.1, 0.2, ... 0.
|
|
14
|
+
"description": "Schema version (0.1, 0.2, ... 0.6). Bumped when fields are added or behavior changes. Migrate with `docguard upgrade --apply`.",
|
|
15
15
|
"pattern": "^\\d+\\.\\d+(\\.\\d+)?$"
|
|
16
16
|
},
|
|
17
17
|
"projectName": {
|
|
@@ -112,6 +112,9 @@
|
|
|
112
112
|
"generatedStaleness":{ "type": "boolean" },
|
|
113
113
|
"canonicalSync": { "type": "boolean" },
|
|
114
114
|
"surfaceSync": { "type": "boolean" },
|
|
115
|
+
"diffSuspicion": { "type": "boolean" },
|
|
116
|
+
"referenceExistence":{ "type": "boolean" },
|
|
117
|
+
"apiDocSmells": { "type": "boolean" },
|
|
115
118
|
"metricsConsistency":{ "type": "boolean" }
|
|
116
119
|
},
|
|
117
120
|
"additionalProperties": false
|
|
@@ -124,6 +127,17 @@
|
|
|
124
127
|
"enum": ["high", "medium", "low"]
|
|
125
128
|
}
|
|
126
129
|
},
|
|
130
|
+
"findingSeverity": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"description": "Per-finding-code enforcement overrides. Exact codes take precedence over validator severity. high = blocking error, medium = warning, low = informational. Intrinsic errors can only be demoted with an explicit code entry.",
|
|
133
|
+
"patternProperties": {
|
|
134
|
+
"^[A-Z]{3}[0-9]{3}$": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"enum": ["high", "medium", "low"]
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"additionalProperties": false
|
|
140
|
+
},
|
|
127
141
|
"draftStalenessDays": {
|
|
128
142
|
"type": "integer",
|
|
129
143
|
"minimum": 1,
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"oneOf": [
|
|
49
49
|
{ "$ref": "#/$defs/jsonSource" },
|
|
50
50
|
{ "$ref": "#/$defs/collectionSource" },
|
|
51
|
+
{ "$ref": "#/$defs/pythonLiteralSource" },
|
|
51
52
|
{ "$ref": "#/$defs/oasdiffSource" },
|
|
52
53
|
{ "$ref": "#/$defs/bufSource" }
|
|
53
54
|
]
|
|
@@ -82,6 +83,17 @@
|
|
|
82
83
|
"allowEmpty": { "type": "boolean" }
|
|
83
84
|
}
|
|
84
85
|
},
|
|
86
|
+
"pythonLiteralSource": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"additionalProperties": false,
|
|
89
|
+
"required": ["adapter", "path", "symbol", "allowEmpty"],
|
|
90
|
+
"properties": {
|
|
91
|
+
"adapter": { "const": "python-literal-count" },
|
|
92
|
+
"path": { "allOf": [{ "$ref": "#/$defs/safePath" }, { "pattern": "\\.py$" }] },
|
|
93
|
+
"symbol": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,127}$" },
|
|
94
|
+
"allowEmpty": { "type": "boolean" }
|
|
95
|
+
}
|
|
96
|
+
},
|
|
85
97
|
"input": {
|
|
86
98
|
"type": "object",
|
|
87
99
|
"additionalProperties": false,
|
|
@@ -16,6 +16,22 @@
|
|
|
16
16
|
"pointer": "/retentionDays"
|
|
17
17
|
},
|
|
18
18
|
"predicate": { "kind": "equals", "valueType": "number" }
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "surface.python-scanners",
|
|
22
|
+
"applicability": { "mode": "always" },
|
|
23
|
+
"target": {
|
|
24
|
+
"document": "docs-canonical/ARCHITECTURE.md",
|
|
25
|
+
"heading": "Scanner registry",
|
|
26
|
+
"statement": "The registry contains {{value}} scanners."
|
|
27
|
+
},
|
|
28
|
+
"source": {
|
|
29
|
+
"adapter": "python-literal-count",
|
|
30
|
+
"path": "src/package/scanners.py",
|
|
31
|
+
"symbol": "SCANNERS",
|
|
32
|
+
"allowEmpty": false
|
|
33
|
+
},
|
|
34
|
+
"predicate": { "kind": "count-equals" }
|
|
19
35
|
}
|
|
20
36
|
]
|
|
21
37
|
}
|