@xenos1996/usa 2.25.3 → 2.26.2
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/USA.md +3 -0
- package/dist/cli-audit.d.ts.map +1 -1
- package/dist/cli-audit.js +6 -0
- package/dist/cli-audit.js.map +1 -1
- package/dist/cli-docs.d.ts +18 -0
- package/dist/cli-docs.d.ts.map +1 -0
- package/dist/cli-docs.js +416 -0
- package/dist/cli-docs.js.map +1 -0
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +18 -0
- package/dist/cli-help.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -1
- package/dist/config.js.map +1 -1
- package/dist/engine/audit.d.ts +8 -0
- package/dist/engine/audit.d.ts.map +1 -1
- package/dist/engine/audit.js +63 -0
- package/dist/engine/audit.js.map +1 -1
- package/dist/engine/docs-audit.d.ts +125 -0
- package/dist/engine/docs-audit.d.ts.map +1 -0
- package/dist/engine/docs-audit.js +405 -0
- package/dist/engine/docs-audit.js.map +1 -0
- package/dist/engine/docs-impact.d.ts +62 -0
- package/dist/engine/docs-impact.d.ts.map +1 -0
- package/dist/engine/docs-impact.js +165 -0
- package/dist/engine/docs-impact.js.map +1 -0
- package/dist/engine/docs-invariants.d.ts +66 -0
- package/dist/engine/docs-invariants.d.ts.map +1 -0
- package/dist/engine/docs-invariants.js +667 -0
- package/dist/engine/docs-invariants.js.map +1 -0
- package/dist/engine/docs-taxonomy.d.ts +72 -0
- package/dist/engine/docs-taxonomy.d.ts.map +1 -0
- package/dist/engine/docs-taxonomy.js +294 -0
- package/dist/engine/docs-taxonomy.js.map +1 -0
- package/dist/engine/gate.d.ts +5 -1
- package/dist/engine/gate.d.ts.map +1 -1
- package/dist/engine/gate.js +14 -3
- package/dist/engine/gate.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/util/project.d.ts +4 -0
- package/dist/util/project.d.ts.map +1 -1
- package/dist/util/project.js +9 -1
- package/dist/util/project.js.map +1 -1
- package/package.json +9 -5
- package/rules/core/security.yaml +1 -1
- package/rules/docs-taxonomy.yaml +3362 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation universe audit — orchestration.
|
|
3
|
+
*
|
|
4
|
+
* `runDocAudit` is the single deterministic computation behind three surfaces
|
|
5
|
+
* that must never disagree (AC-8):
|
|
6
|
+
* 1. `usa docs audit` — standalone report (DOC-AUDIT.md / JSON)
|
|
7
|
+
* 2. `usa audit --docs-universe` — DOCU-* findings inside the main report
|
|
8
|
+
* 3. the `--fail-on` gate on either surface (same findings object)
|
|
9
|
+
*
|
|
10
|
+
* It composes:
|
|
11
|
+
* - the taxonomy (rules/docs-taxonomy.yaml, 14 categories / 220 artifacts)
|
|
12
|
+
* - the inventory (presence per artifact, tier expectation per maturity)
|
|
13
|
+
* - the invariants (src/engine/docs-invariants.ts)
|
|
14
|
+
* - the impact analysis (src/engine/docs-impact.ts, when --changed)
|
|
15
|
+
*/
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { Project } from '../util/project.js';
|
|
18
|
+
import { detect, loadDetectorFile } from '../detect/index.js';
|
|
19
|
+
import { EXPECTED_TIERS, TIER_SEVERITY, artifactApplies, loadDocTaxonomy, matchPatterns, } from './docs-taxonomy.js';
|
|
20
|
+
import { checkBrokenLinks, checkChangelogConsistency, checkDocstringCoverage, checkEnvVarCoverage, checkGeneratedProvenance, checkRouteCoverage, invariantToFinding, } from './docs-invariants.js';
|
|
21
|
+
import { analyzeImpact } from './docs-impact.js';
|
|
22
|
+
/** Static limitations, reported honestly in every report. */
|
|
23
|
+
export const DOC_LIMITATIONS = [
|
|
24
|
+
'Presence is pattern-based: an artifact can exist under a name no pattern covers (reported as MANUAL when no pattern is defined, otherwise as MISSING).',
|
|
25
|
+
'Impact sources are declarative globs plus any-source; a change whose invalidation path is not declared in rules/docs-taxonomy.yaml is not flagged.',
|
|
26
|
+
'Route and env-var extraction are framework-broad heuristics (Express/FastAPI/Flask/chi-style signatures); exotic routers may be missed, so undetected surfaces are the only blind spot.',
|
|
27
|
+
'USA is a read-only auditor: it reports remediation but never rewrites the target repository (the sync half of the contract is the agent/human job).',
|
|
28
|
+
'Cross-references are checked for file existence, not for semantic correctness of the reference.',
|
|
29
|
+
];
|
|
30
|
+
/* --------------------------------------------------------------- inventory -- */
|
|
31
|
+
export function buildInventory(project, taxonomy, facts, expectedTiers) {
|
|
32
|
+
const entries = new Map();
|
|
33
|
+
const byCat = new Map();
|
|
34
|
+
for (const a of taxonomy.artifacts) {
|
|
35
|
+
const applies = artifactApplies(a, facts);
|
|
36
|
+
let status;
|
|
37
|
+
let files = [];
|
|
38
|
+
if (!applies)
|
|
39
|
+
status = 'NOT_APPLICABLE';
|
|
40
|
+
else if (a.patterns.length === 0)
|
|
41
|
+
status = 'MANUAL';
|
|
42
|
+
else {
|
|
43
|
+
files = matchPatterns(project.files, a.patterns);
|
|
44
|
+
status = files.length > 0 ? 'PRESENT' : 'MISSING';
|
|
45
|
+
}
|
|
46
|
+
const entry = {
|
|
47
|
+
artifact: a,
|
|
48
|
+
status,
|
|
49
|
+
expected: applies && expectedTiers.includes(a.tier),
|
|
50
|
+
files,
|
|
51
|
+
aliasRule: status === 'MISSING' ? a.aliasRule : undefined,
|
|
52
|
+
};
|
|
53
|
+
entries.set(a.id, entry);
|
|
54
|
+
const list = byCat.get(a.cat) ?? [];
|
|
55
|
+
list.push(entry);
|
|
56
|
+
byCat.set(a.cat, list);
|
|
57
|
+
}
|
|
58
|
+
const inventory = taxonomy.categories.map((c) => {
|
|
59
|
+
const list = byCat.get(c.id) ?? [];
|
|
60
|
+
return {
|
|
61
|
+
id: c.id,
|
|
62
|
+
name: c.name,
|
|
63
|
+
entries: list,
|
|
64
|
+
present: list.filter((e) => e.status === 'PRESENT').length,
|
|
65
|
+
missing: list.filter((e) => e.status === 'MISSING').length,
|
|
66
|
+
manual: list.filter((e) => e.status === 'MANUAL').length,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
const coverage = [0, 1, 2, 3, 4].map((tier) => {
|
|
70
|
+
const list = [...entries.values()].filter((e) => e.artifact.tier === tier && e.status !== 'NOT_APPLICABLE');
|
|
71
|
+
return {
|
|
72
|
+
tier,
|
|
73
|
+
applicable: list.length,
|
|
74
|
+
present: list.filter((e) => e.status === 'PRESENT').length,
|
|
75
|
+
missing: list.filter((e) => e.status === 'MISSING').length,
|
|
76
|
+
manual: list.filter((e) => e.status === 'MANUAL').length,
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
return { inventory, coverage, entries };
|
|
80
|
+
}
|
|
81
|
+
/* --------------------------------------------------------------- invariants -- */
|
|
82
|
+
export function runInvariants(project, facts, gitTags) {
|
|
83
|
+
return [
|
|
84
|
+
checkEnvVarCoverage(project),
|
|
85
|
+
checkRouteCoverage(project),
|
|
86
|
+
checkDocstringCoverage(project, facts),
|
|
87
|
+
checkBrokenLinks(project),
|
|
88
|
+
checkGeneratedProvenance(project),
|
|
89
|
+
checkChangelogConsistency(project, gitTags),
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The DOCU finding set: one finding per missing-expected artifact (aliased
|
|
94
|
+
* absences suppressed per options) plus one finding per failing invariant.
|
|
95
|
+
* Pure function of the audit state → deterministic (AC-2) and shared by the
|
|
96
|
+
* standalone report and the main audit injection (AC-8).
|
|
97
|
+
*/
|
|
98
|
+
export function docFindings(result, opts) {
|
|
99
|
+
const catName = new Map(result.inventory.map((c) => [c.id, c.name]));
|
|
100
|
+
const findings = [];
|
|
101
|
+
for (const e of collectEntries(result)) {
|
|
102
|
+
const f = missingArtifactFinding(e, catName.get(e.artifact.cat) ?? '', result, opts);
|
|
103
|
+
if (f)
|
|
104
|
+
findings.push(f);
|
|
105
|
+
}
|
|
106
|
+
for (const r of result.invariants) {
|
|
107
|
+
if (r.status !== 'FAIL')
|
|
108
|
+
continue;
|
|
109
|
+
const { severity, dampened } = opts.dampen('LOW', null);
|
|
110
|
+
findings.push(invariantToFinding(r, severity, dampened));
|
|
111
|
+
}
|
|
112
|
+
return findings;
|
|
113
|
+
}
|
|
114
|
+
function collectEntries(result) {
|
|
115
|
+
const entries = new Map();
|
|
116
|
+
for (const cat of result.inventory)
|
|
117
|
+
for (const e of cat.entries)
|
|
118
|
+
entries.set(e.artifact.id, e);
|
|
119
|
+
return [...entries.values()].sort((a, b) => a.artifact.id - b.artifact.id);
|
|
120
|
+
}
|
|
121
|
+
function missingArtifactFinding(e, catLabel, result, opts) {
|
|
122
|
+
if (e.status !== 'MISSING' || !e.expected)
|
|
123
|
+
return null;
|
|
124
|
+
if (e.aliasRule && opts.suppressAliasedRules?.has(e.aliasRule))
|
|
125
|
+
return null;
|
|
126
|
+
const base = TIER_SEVERITY[e.artifact.tier] ?? 'LOW';
|
|
127
|
+
const { severity, dampened } = opts.dampen(base, null);
|
|
128
|
+
const a = e.artifact;
|
|
129
|
+
return {
|
|
130
|
+
ruleId: `DOCU-${a.id}`,
|
|
131
|
+
title: `${a.name} is missing`,
|
|
132
|
+
section: 'S12',
|
|
133
|
+
sectionTitle: 'Documentation & Knowledge',
|
|
134
|
+
severity,
|
|
135
|
+
baseSeverity: base,
|
|
136
|
+
status: 'MISSING',
|
|
137
|
+
ruleClass: 'documentation',
|
|
138
|
+
dampened,
|
|
139
|
+
message: `Artifact "${a.name}" (${a.cat} ${catLabel} #${a.id}, tier ${a.tier}, ` +
|
|
140
|
+
`${a.kind}) is expected at ${result.maturity} stage but no matching file was found.`,
|
|
141
|
+
locations: [],
|
|
142
|
+
why: 'Documentation is a first-class correctness contract: a missing expected artifact is a silent obligation, ' +
|
|
143
|
+
'and the contract is tiered so young projects are not graded against launch bars.',
|
|
144
|
+
remediation: remediationHint(a),
|
|
145
|
+
references: ['Docs-as-Code'],
|
|
146
|
+
automatability: 'full',
|
|
147
|
+
tags: [`doc-cat:${a.cat}`, `doc-tier:${a.tier}`, `doc-kind:${a.kind}`],
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function remediationHint(a) {
|
|
151
|
+
const positive = a.patterns.filter((p) => !p.startsWith('!'));
|
|
152
|
+
const hint = positive.length > 0
|
|
153
|
+
? `Add the artifact (looked for: ${positive.slice(0, 5).join(', ')}).`
|
|
154
|
+
: 'Add the artifact (not mechanically trackable — record where it lives).';
|
|
155
|
+
return a.notes ? `${hint} ${a.notes}` : hint;
|
|
156
|
+
}
|
|
157
|
+
/* ------------------------------------------------------------------ render -- */
|
|
158
|
+
const STATUS_ICON = {
|
|
159
|
+
PRESENT: '✅',
|
|
160
|
+
MISSING: '🚫',
|
|
161
|
+
MANUAL: '🖐️',
|
|
162
|
+
NOT_APPLICABLE: '➖',
|
|
163
|
+
};
|
|
164
|
+
/** Standalone markdown report (DOC-AUDIT.md). Deterministic except the date. */
|
|
165
|
+
export function renderDocAuditMd(result, today) {
|
|
166
|
+
return [
|
|
167
|
+
renderHeader(result, today),
|
|
168
|
+
renderVerificationTable(result),
|
|
169
|
+
renderTierTable(result),
|
|
170
|
+
renderUniverse(result),
|
|
171
|
+
renderInvariants(result),
|
|
172
|
+
result.impact ? renderImpact(result.impact) : '',
|
|
173
|
+
renderFindings(result),
|
|
174
|
+
renderLimitations(result),
|
|
175
|
+
]
|
|
176
|
+
.filter((s) => s.length > 0)
|
|
177
|
+
.join('\n');
|
|
178
|
+
}
|
|
179
|
+
function renderHeader(result, today) {
|
|
180
|
+
const L = [];
|
|
181
|
+
L.push(`# Documentation audit — ${result.target}`);
|
|
182
|
+
L.push('');
|
|
183
|
+
L.push(`> USA ${result.usaVersion} · taxonomy ${result.taxonomy.schema} ` +
|
|
184
|
+
`(${result.taxonomy.artifacts} artifacts, ${result.taxonomy.categories} categories)`);
|
|
185
|
+
if (result.commit)
|
|
186
|
+
L.push(`> Baseline: \`${result.commit}\``);
|
|
187
|
+
L.push(`> Maturity: **${result.maturity}** · expected tiers: ${result.expectedTiers.join(', ')} · ${today}`);
|
|
188
|
+
return L.join('\n');
|
|
189
|
+
}
|
|
190
|
+
function renderVerificationTable(result) {
|
|
191
|
+
const L = ['## Verification report', '', '| Metric | Value |', '| ------ | ----- |'];
|
|
192
|
+
L.push(`| Baseline | ${result.commit ?? 'not a git repo'} |`);
|
|
193
|
+
L.push(`| Categories covered | ${result.taxonomy.categories}/14 |`);
|
|
194
|
+
for (const c of result.coverage) {
|
|
195
|
+
L.push(`| Tier ${c.tier} artifacts | ${c.present} present / ${c.applicable} applicable ` +
|
|
196
|
+
`(missing ${c.missing}, manual ${c.manual}) |`);
|
|
197
|
+
}
|
|
198
|
+
const failing = result.invariants.filter((i) => i.status === 'FAIL').length;
|
|
199
|
+
L.push(`| Invariants | ${result.invariants.length - failing}/${result.invariants.length} passing |`);
|
|
200
|
+
const crossCats = result.impact?.affected.filter((a) => a.crossCategory).length ?? 0;
|
|
201
|
+
L.push(`| Impact mechanism | taxonomy sources + derives_from/syncs_with edges` +
|
|
202
|
+
(result.impact
|
|
203
|
+
? ` (this run: ${result.impact.affected.length} affected, ${crossCats} cross-category)`
|
|
204
|
+
: '') +
|
|
205
|
+
' |');
|
|
206
|
+
L.push(`| Local commands | \`usa docs audit\`, \`usa docs impact\`, \`usa docs coverage\`, \`usa audit --docs-universe\` |`);
|
|
207
|
+
L.push(`| Gate | \`usa docs audit --fail-on <sev>\` (same findings as CI) |`);
|
|
208
|
+
L.push(`| Open findings | ${result.findings.length} |`);
|
|
209
|
+
return L.join('\n');
|
|
210
|
+
}
|
|
211
|
+
function renderTierTable(result) {
|
|
212
|
+
const L = [
|
|
213
|
+
'## Tier coverage',
|
|
214
|
+
'',
|
|
215
|
+
'| Tier | Applicable | Present | Missing | Manual | Expected |',
|
|
216
|
+
'| ---- | ---------- | ------- | ------- | ------ | -------- |',
|
|
217
|
+
];
|
|
218
|
+
for (const c of result.coverage) {
|
|
219
|
+
L.push(`| ${c.tier} | ${c.applicable} | ${c.present} | ${c.missing} | ${c.manual} | ` +
|
|
220
|
+
`${result.expectedTiers.includes(c.tier) ? 'yes' : 'no'} |`);
|
|
221
|
+
}
|
|
222
|
+
const t = result.totals;
|
|
223
|
+
L.push('');
|
|
224
|
+
L.push(`Totals: ${t.present} present, ${t.missing} missing, ${t.manual} manual, ` +
|
|
225
|
+
`${t.notApplicable} not applicable (of ${t.applicable} applicable).`);
|
|
226
|
+
return L.join('\n');
|
|
227
|
+
}
|
|
228
|
+
function renderUniverse(result) {
|
|
229
|
+
const L = ['## Documentation universe'];
|
|
230
|
+
for (const cat of result.inventory) {
|
|
231
|
+
L.push('');
|
|
232
|
+
L.push(`### ${cat.id} · ${cat.name} — ${cat.present} present / ${cat.missing} missing / ${cat.manual} manual`);
|
|
233
|
+
L.push('');
|
|
234
|
+
L.push('| # | Artifact | Tier | Kind | Status | Evidence |');
|
|
235
|
+
L.push('| - | -------- | ---- | ---- | ------ | -------- |');
|
|
236
|
+
for (const e of cat.entries)
|
|
237
|
+
L.push(universeRow(e));
|
|
238
|
+
}
|
|
239
|
+
return L.join('\n');
|
|
240
|
+
}
|
|
241
|
+
function universeRow(e) {
|
|
242
|
+
const a = e.artifact;
|
|
243
|
+
const icon = STATUS_ICON[e.status];
|
|
244
|
+
const flag = e.expected && e.status === 'MISSING' ? ' **(expected)**' : '';
|
|
245
|
+
const alias = e.aliasRule ? ` _alias: ${e.aliasRule}_` : '';
|
|
246
|
+
const evidence = e.status === 'PRESENT'
|
|
247
|
+
? e.files.slice(0, 3).join(', ') + (e.files.length > 3 ? ` (+${e.files.length - 3})` : '')
|
|
248
|
+
: e.status === 'MANUAL'
|
|
249
|
+
? 'manual'
|
|
250
|
+
: e.status === 'MISSING'
|
|
251
|
+
? '—'
|
|
252
|
+
: 'n/a';
|
|
253
|
+
return `| ${a.id} | ${a.name} | ${a.tier} | ${a.kind} | ${icon} ${e.status}${flag}${alias} | ${evidence} |`;
|
|
254
|
+
}
|
|
255
|
+
function renderInvariants(result) {
|
|
256
|
+
const L = [
|
|
257
|
+
'## Invariants',
|
|
258
|
+
'',
|
|
259
|
+
'| Invariant | Status | Detail |',
|
|
260
|
+
'| --------- | ------ | ------ |',
|
|
261
|
+
];
|
|
262
|
+
for (const r of result.invariants) {
|
|
263
|
+
const icon = r.status === 'PASS' ? '✅' : r.status === 'FAIL' ? '❌' : '➖';
|
|
264
|
+
L.push(`| ${r.id} — ${r.title} | ${icon} ${r.status} | ${r.detail} |`);
|
|
265
|
+
}
|
|
266
|
+
return L.join('\n');
|
|
267
|
+
}
|
|
268
|
+
function renderImpact(impact) {
|
|
269
|
+
const L = [
|
|
270
|
+
'## Impact analysis',
|
|
271
|
+
'',
|
|
272
|
+
`Changed files: ${impact.changed.length} · affected artifacts: ${impact.affected.length} · ` +
|
|
273
|
+
`edges traversed: ${impact.edgesTraversed}`,
|
|
274
|
+
'',
|
|
275
|
+
];
|
|
276
|
+
if (impact.affected.length > 0) {
|
|
277
|
+
L.push('| Artifact | Category | Tier | Cross-category | Reason |');
|
|
278
|
+
L.push('| -------- | -------- | ---- | -------------- | ------ |');
|
|
279
|
+
for (const e of impact.affected) {
|
|
280
|
+
L.push(`| #${e.artifact.id} ${e.artifact.name} | ${e.artifact.cat} | ${e.artifact.tier} | ` +
|
|
281
|
+
`${e.crossCategory ? 'yes' : 'no'} | ${e.reasons.join('; ')} |`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (impact.noImpact.length > 0) {
|
|
285
|
+
L.push('');
|
|
286
|
+
L.push(`No documentation impact detected for: ${impact.noImpact.join(', ')}`);
|
|
287
|
+
}
|
|
288
|
+
return L.join('\n');
|
|
289
|
+
}
|
|
290
|
+
function renderFindings(result) {
|
|
291
|
+
const L = ['## Open findings', ''];
|
|
292
|
+
if (result.findings.length === 0) {
|
|
293
|
+
L.push('No open findings.');
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
for (const f of result.findings)
|
|
297
|
+
L.push(findingBlock(f));
|
|
298
|
+
}
|
|
299
|
+
return L.join('\n');
|
|
300
|
+
}
|
|
301
|
+
function findingBlock(f) {
|
|
302
|
+
const L = [`- **${f.severity} ${f.ruleId}** — ${f.title}`, ` ${f.message}`];
|
|
303
|
+
for (const loc of f.locations.slice(0, 10)) {
|
|
304
|
+
L.push(` - ${loc.file}${loc.line ? `:${loc.line}` : ''}${loc.excerpt ? ` — ${loc.excerpt}` : ''}`);
|
|
305
|
+
}
|
|
306
|
+
if (f.locations.length > 10)
|
|
307
|
+
L.push(` - … and ${f.locations.length - 10} more`);
|
|
308
|
+
if (f.remediation)
|
|
309
|
+
L.push(` _Repair: ${f.remediation}_`);
|
|
310
|
+
return L.join('\n');
|
|
311
|
+
}
|
|
312
|
+
function renderLimitations(result) {
|
|
313
|
+
const L = ['## Known limitations', ''];
|
|
314
|
+
for (const lim of result.limitations)
|
|
315
|
+
L.push(`- ${lim}`);
|
|
316
|
+
return L.join('\n');
|
|
317
|
+
}
|
|
318
|
+
/* ------------------------------------------------------------------ driver -- */
|
|
319
|
+
/** The one deterministic computation (see module doc). */
|
|
320
|
+
export function runDocAudit(input) {
|
|
321
|
+
const expectedTiers = input.tiers ?? EXPECTED_TIERS[input.maturity];
|
|
322
|
+
const { inventory, coverage, entries } = buildInventory(input.project, input.taxonomy, input.facts, expectedTiers);
|
|
323
|
+
const invariants = runInvariants(input.project, input.facts, input.gitTags ?? []);
|
|
324
|
+
const impact = input.changedFiles && input.changedFiles.length > 0
|
|
325
|
+
? analyzeImpact(input.taxonomy, input.facts, input.changedFiles)
|
|
326
|
+
: undefined;
|
|
327
|
+
const totals = {
|
|
328
|
+
artifacts: input.taxonomy.artifacts.length,
|
|
329
|
+
applicable: 0,
|
|
330
|
+
present: 0,
|
|
331
|
+
missing: 0,
|
|
332
|
+
manual: 0,
|
|
333
|
+
notApplicable: 0,
|
|
334
|
+
};
|
|
335
|
+
for (const e of entries.values()) {
|
|
336
|
+
if (e.status === 'NOT_APPLICABLE') {
|
|
337
|
+
totals.notApplicable++;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
totals.applicable++;
|
|
341
|
+
if (e.status === 'PRESENT')
|
|
342
|
+
totals.present++;
|
|
343
|
+
else if (e.status === 'MISSING')
|
|
344
|
+
totals.missing++;
|
|
345
|
+
else
|
|
346
|
+
totals.manual++;
|
|
347
|
+
}
|
|
348
|
+
const result = {
|
|
349
|
+
schema: 'usa-doc-audit-v1',
|
|
350
|
+
target: input.target,
|
|
351
|
+
commit: input.commit,
|
|
352
|
+
usaVersion: input.usaVersion,
|
|
353
|
+
taxonomy: {
|
|
354
|
+
schema: input.taxonomy.schema,
|
|
355
|
+
categories: input.taxonomy.categories.length,
|
|
356
|
+
artifacts: input.taxonomy.artifacts.length,
|
|
357
|
+
},
|
|
358
|
+
maturity: input.maturity,
|
|
359
|
+
expectedTiers,
|
|
360
|
+
inventory,
|
|
361
|
+
coverage,
|
|
362
|
+
totals,
|
|
363
|
+
invariants,
|
|
364
|
+
impact,
|
|
365
|
+
findings: [],
|
|
366
|
+
limitations: [...DOC_LIMITATIONS],
|
|
367
|
+
};
|
|
368
|
+
// Identity dampening here; the main audit re-derives findings with its own
|
|
369
|
+
// profile via `docFindings` — same inputs, same outputs (AC-8).
|
|
370
|
+
result.findings = docFindings(result, {
|
|
371
|
+
dampen: (base) => ({ severity: base, dampened: false }),
|
|
372
|
+
});
|
|
373
|
+
return result;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Shared load: project index + detection + taxonomy. Used by the CLI so the
|
|
377
|
+
* three `usa docs` subcommands and `usa audit --docs-universe` share one
|
|
378
|
+
* loading path (single source of truth for the inputs, AC-8).
|
|
379
|
+
*/
|
|
380
|
+
export function loadDocAuditContext(target, rulesDir, extraFacts = []) {
|
|
381
|
+
const warnings = [];
|
|
382
|
+
const project = new Project(target);
|
|
383
|
+
const git = project.gitInfo();
|
|
384
|
+
const detection = detect(project, loadDetectorFile(rulesDir), git, extraFacts);
|
|
385
|
+
const { taxonomy, warnings: taxWarnings } = loadDocTaxonomy(rulesDir);
|
|
386
|
+
warnings.push(...taxWarnings);
|
|
387
|
+
if (!taxonomy)
|
|
388
|
+
return { context: null, taxonomy: null, warnings };
|
|
389
|
+
return {
|
|
390
|
+
context: {
|
|
391
|
+
project,
|
|
392
|
+
facts: detection.facts,
|
|
393
|
+
maturity: detection.maturity,
|
|
394
|
+
warnings,
|
|
395
|
+
commit: git.commit,
|
|
396
|
+
gitTags: project.gitTags(),
|
|
397
|
+
},
|
|
398
|
+
taxonomy,
|
|
399
|
+
warnings,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
export function resolveTargetLabel(target) {
|
|
403
|
+
return path.basename(path.resolve(target)) || target;
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=docs-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-audit.js","sourceRoot":"","sources":["../../src/engine/docs-audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation impact analysis — semantic dependency model.
|
|
3
|
+
*
|
|
4
|
+
* Answers the Phase 3 questions with deterministic mechanisms:
|
|
5
|
+
* - What docs (across all 14 categories) are affected by changing file X?
|
|
6
|
+
* - Which sources does document Y describe?
|
|
7
|
+
*
|
|
8
|
+
* Impact classes, all computed from the taxonomy's declarative edges:
|
|
9
|
+
* direct source → its documentation (artifact.sources match)
|
|
10
|
+
* transitive source → intermediate artifact → downstream docs
|
|
11
|
+
* (BFS over derives_from edges, bounded)
|
|
12
|
+
* cross-category any edge that crosses a category boundary is flagged
|
|
13
|
+
* sync peers declared in syncs_with must move together
|
|
14
|
+
*
|
|
15
|
+
* Conservative by construction (AC-11): `any-source` artifacts (README,
|
|
16
|
+
* changelog, user guide) are hit by every non-document change, and a changed
|
|
17
|
+
* document invalidates its sync partners and everything that derives from it.
|
|
18
|
+
* Uncertainty resolves to "affected", never to "fine".
|
|
19
|
+
*/
|
|
20
|
+
import type { Facts } from '../types.js';
|
|
21
|
+
import { type DocArtifact, type DocTaxonomy } from './docs-taxonomy.js';
|
|
22
|
+
export interface ImpactEntry {
|
|
23
|
+
artifact: DocArtifact;
|
|
24
|
+
/** Human-readable reasons, sorted (deterministic). */
|
|
25
|
+
reasons: string[];
|
|
26
|
+
/** Changed files that triggered the impact, sorted. */
|
|
27
|
+
triggeredBy: string[];
|
|
28
|
+
/** True when at least one traversed edge crossed a category boundary. */
|
|
29
|
+
crossCategory: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface ImpactResult {
|
|
32
|
+
changed: string[];
|
|
33
|
+
affected: ImpactEntry[];
|
|
34
|
+
/** Changed files with no detected documentation impact. */
|
|
35
|
+
noImpact: string[];
|
|
36
|
+
/** Number of dependency edges traversed during the transitive walk. */
|
|
37
|
+
edgesTraversed: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Compute the documentation impact of a set of changed files.
|
|
41
|
+
*
|
|
42
|
+
* Pure function of (project index, taxonomy, facts, changed files) — the
|
|
43
|
+
* same change set always yields the same impact report (AC-2), and the
|
|
44
|
+
* computation is O(artifacts × changed × globs): bounded and fast.
|
|
45
|
+
*/
|
|
46
|
+
export declare function analyzeImpact(taxonomy: DocTaxonomy, facts: Facts, changedFiles: string[]): ImpactResult;
|
|
47
|
+
export interface DescribeResult {
|
|
48
|
+
file: string;
|
|
49
|
+
/** Artifacts whose source globs this file can invalidate. */
|
|
50
|
+
invalidates: DocArtifact[];
|
|
51
|
+
/** Artifacts this file is a document of (its own presence patterns). */
|
|
52
|
+
documents: DocArtifact[];
|
|
53
|
+
/** Downstream artifacts that derive from documents of this file. */
|
|
54
|
+
downstream: DocArtifact[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Reverse query: for one file, which documentation relationship exists?
|
|
58
|
+
* "Which sources does document Y describe?" is `invalidates`; "what does Y
|
|
59
|
+
* feed?" is `downstream`.
|
|
60
|
+
*/
|
|
61
|
+
export declare function describeFile(taxonomy: DocTaxonomy, facts: Facts, file: string): DescribeResult;
|
|
62
|
+
//# sourceMappingURL=docs-impact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-impact.d.ts","sourceRoot":"","sources":["../../src/engine/docs-impact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,WAAW,CAAC;IACtB,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,yEAAyE;IACzE,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uEAAuE;IACvE,cAAc,EAAE,MAAM,CAAC;CACxB;AAqJD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,KAAK,EACZ,YAAY,EAAE,MAAM,EAAE,GACrB,YAAY,CAQd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,wEAAwE;IACxE,SAAS,EAAE,WAAW,EAAE,CAAC;IACzB,oEAAoE;IACpE,UAAU,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CAwB9F"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { matchesGlob } from '../util/glob.js';
|
|
2
|
+
import { artifactApplies, matchPatterns, } from './docs-taxonomy.js';
|
|
3
|
+
const DOC_FILE_RE = /\.(md|mdx|rst|txt|adoc)$/i;
|
|
4
|
+
function isDocFile(file) {
|
|
5
|
+
return DOC_FILE_RE.test(file) || /^README/i.test(basename(file)) || file.startsWith('docs/');
|
|
6
|
+
}
|
|
7
|
+
function basename(p) {
|
|
8
|
+
const i = p.lastIndexOf('/');
|
|
9
|
+
return i === -1 ? p : p.slice(i + 1);
|
|
10
|
+
}
|
|
11
|
+
/** True when a changed file matches any of the artifact's presence globs. */
|
|
12
|
+
function changedDocOf(file, a) {
|
|
13
|
+
return matchPatterns([file], a.patterns).length > 0;
|
|
14
|
+
}
|
|
15
|
+
/** Adjacency: upstream id → dependents (D derives_from A ⇒ A's change hits D). */
|
|
16
|
+
function buildDependents(taxonomy) {
|
|
17
|
+
const dependents = new Map();
|
|
18
|
+
for (const a of taxonomy.artifacts) {
|
|
19
|
+
for (const up of a.derivesFrom) {
|
|
20
|
+
const arr = dependents.get(up) ?? [];
|
|
21
|
+
arr.push(a.id);
|
|
22
|
+
dependents.set(up, arr);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return dependents;
|
|
26
|
+
}
|
|
27
|
+
function makeTouch(hit) {
|
|
28
|
+
return (id, reason, file, cross = false) => {
|
|
29
|
+
const e = hit.get(id) ?? {
|
|
30
|
+
reasons: new Set(),
|
|
31
|
+
triggeredBy: new Set(),
|
|
32
|
+
crossCategory: false,
|
|
33
|
+
};
|
|
34
|
+
e.reasons.add(reason);
|
|
35
|
+
if (file)
|
|
36
|
+
e.triggeredBy.add(file);
|
|
37
|
+
e.crossCategory = e.crossCategory || cross;
|
|
38
|
+
hit.set(id, e);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Pass 1 · direct impact: source globs and self-changed documents. */
|
|
42
|
+
function directHits(changed, applicable, hit) {
|
|
43
|
+
const touch = makeTouch(hit);
|
|
44
|
+
const directIds = new Set();
|
|
45
|
+
for (const f of changed) {
|
|
46
|
+
const doc = isDocFile(f);
|
|
47
|
+
for (const a of applicable) {
|
|
48
|
+
for (const g of a.sources ?? []) {
|
|
49
|
+
if (g === 'any-source') {
|
|
50
|
+
if (!doc)
|
|
51
|
+
touch(a.id, 'any source change invalidates this artifact', f);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (matchesGlob(f, g))
|
|
55
|
+
touch(a.id, `source pattern ${g} matches ${f}`, f);
|
|
56
|
+
}
|
|
57
|
+
if (changedDocOf(f, a))
|
|
58
|
+
touch(a.id, `the document itself changed (${f})`, f);
|
|
59
|
+
directIds.add(a.id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return directIds;
|
|
63
|
+
}
|
|
64
|
+
/** Follow one neighbor edge: record it, queue the target if new. */
|
|
65
|
+
function followEdge(state, from, to, label, queue) {
|
|
66
|
+
const t = state.byId.get(to);
|
|
67
|
+
if (!t)
|
|
68
|
+
return;
|
|
69
|
+
state.edges++;
|
|
70
|
+
const existed = state.hit.has(to);
|
|
71
|
+
makeTouch(state.hit)(to, `${label} ${from.name} (#${from.id})`, undefined, t.cat !== from.cat);
|
|
72
|
+
if (!existed && !state.queued.has(to)) {
|
|
73
|
+
state.queued.add(to);
|
|
74
|
+
queue.push(to);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Pass 2 · transitive closure over derives_from + syncs_with (bounded BFS). */
|
|
78
|
+
function expandClosure(taxonomy, dependents, hit, seed) {
|
|
79
|
+
const queue = [...seed].sort();
|
|
80
|
+
const state = {
|
|
81
|
+
byId: taxonomy.byId,
|
|
82
|
+
dependents,
|
|
83
|
+
hit,
|
|
84
|
+
queued: new Set(queue),
|
|
85
|
+
edges: 0,
|
|
86
|
+
};
|
|
87
|
+
while (queue.length > 0) {
|
|
88
|
+
const a = state.byId.get(queue.shift());
|
|
89
|
+
if (!a)
|
|
90
|
+
continue;
|
|
91
|
+
for (const dep of dependents.get(a.id) ?? [])
|
|
92
|
+
followEdge(state, a, dep, 'derives from', queue);
|
|
93
|
+
for (const partner of a.syncsWith)
|
|
94
|
+
followEdge(state, a, partner, 'syncs with', queue);
|
|
95
|
+
}
|
|
96
|
+
return state.edges;
|
|
97
|
+
}
|
|
98
|
+
function assemble(byId, hit, changed, edges) {
|
|
99
|
+
const affected = [...hit.entries()]
|
|
100
|
+
.map(([id, e]) => ({
|
|
101
|
+
artifact: byId.get(id),
|
|
102
|
+
reasons: [...e.reasons].sort(),
|
|
103
|
+
triggeredBy: [...e.triggeredBy].sort(),
|
|
104
|
+
crossCategory: e.crossCategory,
|
|
105
|
+
}))
|
|
106
|
+
.sort((x, y) => x.artifact.id - y.artifact.id);
|
|
107
|
+
const impactedFiles = new Set();
|
|
108
|
+
for (const e of affected)
|
|
109
|
+
for (const f of e.triggeredBy)
|
|
110
|
+
impactedFiles.add(f);
|
|
111
|
+
return {
|
|
112
|
+
changed,
|
|
113
|
+
affected,
|
|
114
|
+
noImpact: changed.filter((f) => !impactedFiles.has(f)),
|
|
115
|
+
edgesTraversed: edges,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Compute the documentation impact of a set of changed files.
|
|
120
|
+
*
|
|
121
|
+
* Pure function of (project index, taxonomy, facts, changed files) — the
|
|
122
|
+
* same change set always yields the same impact report (AC-2), and the
|
|
123
|
+
* computation is O(artifacts × changed × globs): bounded and fast.
|
|
124
|
+
*/
|
|
125
|
+
export function analyzeImpact(taxonomy, facts, changedFiles) {
|
|
126
|
+
const changed = [...new Set(changedFiles)].sort();
|
|
127
|
+
const applicable = taxonomy.artifacts.filter((a) => artifactApplies(a, facts));
|
|
128
|
+
const hit = new Map();
|
|
129
|
+
const dependents = buildDependents(taxonomy);
|
|
130
|
+
const direct = directHits(changed, applicable, hit);
|
|
131
|
+
const edges = expandClosure(taxonomy, dependents, hit, direct);
|
|
132
|
+
return assemble(taxonomy.byId, hit, changed, edges);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Reverse query: for one file, which documentation relationship exists?
|
|
136
|
+
* "Which sources does document Y describe?" is `invalidates`; "what does Y
|
|
137
|
+
* feed?" is `downstream`.
|
|
138
|
+
*/
|
|
139
|
+
export function describeFile(taxonomy, facts, file) {
|
|
140
|
+
const applicable = taxonomy.artifacts.filter((a) => artifactApplies(a, facts));
|
|
141
|
+
const invalidates = applicable.filter((a) => {
|
|
142
|
+
if (!a.sources)
|
|
143
|
+
return false;
|
|
144
|
+
return a.sources.some((g) => (g === 'any-source' ? !isDocFile(file) : matchesGlob(file, g)));
|
|
145
|
+
});
|
|
146
|
+
const documents = applicable.filter((a) => changedDocOf(file, a));
|
|
147
|
+
const downstream = [];
|
|
148
|
+
const seen = new Set();
|
|
149
|
+
for (const d of documents) {
|
|
150
|
+
const queue = [d.id];
|
|
151
|
+
while (queue.length > 0) {
|
|
152
|
+
const id = queue.shift();
|
|
153
|
+
for (const a of taxonomy.artifacts) {
|
|
154
|
+
if (a.derivesFrom.includes(id) && !seen.has(a.id)) {
|
|
155
|
+
seen.add(a.id);
|
|
156
|
+
downstream.push(a);
|
|
157
|
+
queue.push(a.id);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
downstream.sort((x, y) => x.id - y.id);
|
|
163
|
+
return { file, invalidates, documents, downstream };
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=docs-impact.js.map
|