docgov-cli 0.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-plugin/marketplace.json +29 -0
- package/.claude-plugin/plugin.json +41 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/agents/architect.md +65 -0
- package/agents/classifier.md +44 -0
- package/agents/drift-reviewer.md +59 -0
- package/agents/quality-reviewer.md +59 -0
- package/bin/docgov +1160 -0
- package/bin/docgov.cmd +2 -0
- package/core/check.js +298 -0
- package/core/classify.js +233 -0
- package/core/config.js +162 -0
- package/core/context.js +144 -0
- package/core/document.js +132 -0
- package/core/drift.js +225 -0
- package/core/find.js +61 -0
- package/core/frontmatter.js +65 -0
- package/core/git.js +113 -0
- package/core/graph.js +182 -0
- package/core/health.js +101 -0
- package/core/impact.js +146 -0
- package/core/invariants.js +126 -0
- package/core/inventory.js +167 -0
- package/core/links.js +80 -0
- package/core/migrate.js +158 -0
- package/core/onboard.js +271 -0
- package/core/paths.js +53 -0
- package/core/publish.js +92 -0
- package/core/registry.js +71 -0
- package/core/similarity.js +89 -0
- package/core/size.js +87 -0
- package/core/suppressions.js +58 -0
- package/core/taxonomy.js +477 -0
- package/core/templates.js +159 -0
- package/core/util.js +124 -0
- package/core/yaml.js +250 -0
- package/hooks/hooks.json +65 -0
- package/lenses/agent.md +38 -0
- package/lenses/architecture.md +30 -0
- package/lenses/developer.md +26 -0
- package/lenses/operations.md +32 -0
- package/lenses/readme.md +32 -0
- package/lenses/security.md +33 -0
- package/lenses/user.md +30 -0
- package/package.json +39 -0
- package/policy/documentation.md +82 -0
- package/schemas/config.json +239 -0
- package/schemas/frontmatter.json +299 -0
- package/skills/affected/SKILL.md +41 -0
- package/skills/brief/SKILL.md +38 -0
- package/skills/create/SKILL.md +53 -0
- package/skills/find/SKILL.md +32 -0
- package/skills/health/SKILL.md +36 -0
- package/skills/inspect/SKILL.md +58 -0
- package/skills/publish/SKILL.md +45 -0
- package/skills/review/SKILL.md +65 -0
- package/skills/setup/SKILL.md +52 -0
- package/skills/stale/SKILL.md +55 -0
- package/skills/tag/SKILL.md +59 -0
- package/templates/architecture.adr.md +42 -0
- package/templates/architecture.domain.md +44 -0
- package/templates/architecture.trd.md +72 -0
- package/templates/constitution.invariants.md +40 -0
- package/templates/operations.runbook.md +47 -0
- package/templates/product.prd.md +60 -0
- package/templates/security.threat-model.md +51 -0
- package/templates/user.readme.md +43 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import * as yaml from './yaml.js';
|
|
3
|
+
import { read, write, exists, DocGovError } from './util.js';
|
|
4
|
+
|
|
5
|
+
/** Human override (PRD §40). Recorded, reasoned, optionally expiring — never silent. */
|
|
6
|
+
|
|
7
|
+
export function load(root, cfg) {
|
|
8
|
+
const file = path.join(root, cfg.suppressions_file || '.docgov/suppressions.yaml');
|
|
9
|
+
if (!exists(file)) return { version: 1, suppressions: [] };
|
|
10
|
+
try {
|
|
11
|
+
const s = yaml.parse(read(file)) || {};
|
|
12
|
+
s.suppressions ||= [];
|
|
13
|
+
return s;
|
|
14
|
+
} catch (e) { throw new DocGovError(`suppressions file is not valid: ${e.message}`); }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function save(root, cfg, data) {
|
|
18
|
+
return write(path.join(root, cfg.suppressions_file || '.docgov/suppressions.yaml'), yaml.stringify(data));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function add(root, cfg, { id, reason, expires = null, by = null }) {
|
|
22
|
+
if (!id) throw new DocGovError('suppression needs a finding id');
|
|
23
|
+
if (!reason || String(reason).trim().length < 8) throw new DocGovError('suppression needs a reason of at least 8 characters');
|
|
24
|
+
const data = load(root, cfg);
|
|
25
|
+
const existing = data.suppressions.find((s) => s.id === id);
|
|
26
|
+
const entry = { id, reason: String(reason).trim(), created: new Date().toISOString().slice(0, 10) };
|
|
27
|
+
if (expires) entry.expires = expires;
|
|
28
|
+
if (by) entry.by = by;
|
|
29
|
+
if (existing) Object.assign(existing, entry);
|
|
30
|
+
else data.suppressions.push(entry);
|
|
31
|
+
save(root, cfg, data);
|
|
32
|
+
return entry;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function remove(root, cfg, id) {
|
|
36
|
+
const data = load(root, cfg);
|
|
37
|
+
const before = data.suppressions.length;
|
|
38
|
+
data.suppressions = data.suppressions.filter((s) => s.id !== id);
|
|
39
|
+
save(root, cfg, data);
|
|
40
|
+
return before !== data.suppressions.length;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Split findings into active and suppressed. Expired suppressions do not
|
|
45
|
+
* suppress — they surface as their own warning so they cannot rot quietly.
|
|
46
|
+
*/
|
|
47
|
+
export function apply(findings, data, today = new Date().toISOString().slice(0, 10)) {
|
|
48
|
+
const byId = new Map((data.suppressions || []).map((s) => [s.id, s]));
|
|
49
|
+
const active = [], suppressed = [], expired = [];
|
|
50
|
+
for (const f of findings) {
|
|
51
|
+
const s = byId.get(f.id);
|
|
52
|
+
if (!s) { active.push(f); continue; }
|
|
53
|
+
if (s.expires && s.expires < today) { expired.push({ ...f, suppression: s }); active.push(f); continue; }
|
|
54
|
+
suppressed.push({ ...f, suppression: s });
|
|
55
|
+
}
|
|
56
|
+
const unused = [...byId.values()].filter((s) => !findings.some((f) => f.id === s.id));
|
|
57
|
+
return { active, suppressed, expired, unused };
|
|
58
|
+
}
|
package/core/taxonomy.js
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default documentation taxonomy: authority tiers, document classes, their
|
|
3
|
+
* canonical locations in both layout profiles, size limits, required sections,
|
|
4
|
+
* and quality thresholds.
|
|
5
|
+
*
|
|
6
|
+
* Everything here is data and every field is overridable by .docgov/config.yaml
|
|
7
|
+
* or by a policy pack (V3). Nothing in the engine hardcodes a document class.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** PRD §5 authority model. Lower rank wins a contradiction. */
|
|
11
|
+
export const AUTHORITY = {
|
|
12
|
+
constitution: { rank: 0, tier: 0, label: 'CONSTITUTION' },
|
|
13
|
+
canonical: { rank: 1, tier: 1, label: 'CANONICAL' },
|
|
14
|
+
requirements: { rank: 2, tier: 2, label: 'REQUIREMENTS' },
|
|
15
|
+
decision: { rank: 2, tier: 2, label: 'DECISION' },
|
|
16
|
+
'machine-contract':{ rank: 3, tier: 3, label: 'CONTRACT' },
|
|
17
|
+
implementation: { rank: 4, tier: 4, label: 'CODE' },
|
|
18
|
+
generated: { rank: 5, tier: 5, label: 'GENERATED' },
|
|
19
|
+
audience: { rank: 6, tier: 6, label: 'AUDIENCE' },
|
|
20
|
+
historical: { rank: 7, tier: 7, label: 'HISTORICAL' },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const VISIBILITY = ['public', 'internal', 'confidential', 'generated-public', 'generated-internal'];
|
|
24
|
+
export const STATUS = ['draft', 'active', 'deprecated', 'superseded', 'archived'];
|
|
25
|
+
export const MODES = ['solo', 'team', 'enterprise', 'open-source'];
|
|
26
|
+
|
|
27
|
+
/** PRD §8 relationship edges. `inverse` powers reverse traversal in the graph. */
|
|
28
|
+
export const RELATIONSHIPS = {
|
|
29
|
+
depends_on: { inverse: 'depended_on_by' },
|
|
30
|
+
defines: { inverse: 'defined_by' },
|
|
31
|
+
implements: { inverse: 'implemented_by' },
|
|
32
|
+
derived_from: { inverse: 'derives' },
|
|
33
|
+
supersedes: { inverse: 'superseded_by' },
|
|
34
|
+
references: { inverse: 'referenced_by' },
|
|
35
|
+
validated_by: { inverse: 'validates' },
|
|
36
|
+
generated_from:{ inverse: 'generates' },
|
|
37
|
+
exposes: { inverse: 'exposed_by' },
|
|
38
|
+
documents: { inverse: 'documented_by' },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** PRD §11 audience lenses. Files in lenses/ hold the reviewer prompts. */
|
|
42
|
+
export const LENSES = ['readme', 'developer', 'architecture', 'security', 'user', 'agent', 'operations'];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Document classes.
|
|
46
|
+
* full — path prefix in the numbered taxonomy (PRD §6)
|
|
47
|
+
* compact — path in the small-project layout (PRD §6, second listing)
|
|
48
|
+
* soft/hard — line limits (PRD §13)
|
|
49
|
+
* quality — advisory score threshold (PRD §20)
|
|
50
|
+
* sections — required template headings, deterministic gate (PRD §21)
|
|
51
|
+
*/
|
|
52
|
+
export const TYPES = {
|
|
53
|
+
'constitution.product': {
|
|
54
|
+
label: 'Product Constitution', authority: 'constitution', lens: 'architecture',
|
|
55
|
+
full: 'docs/00-canonical/PRODUCT.md', compact: 'docs/PRODUCT.md', singleton: true,
|
|
56
|
+
soft: 400, hard: 700, quality: 90, visibility: 'internal', template: 'tier0/product',
|
|
57
|
+
sections: ['Purpose', 'Scope', 'Users', 'Invariants', 'Non-goals'],
|
|
58
|
+
},
|
|
59
|
+
'constitution.principles': {
|
|
60
|
+
label: 'Principles', authority: 'constitution', lens: 'architecture',
|
|
61
|
+
full: 'docs/00-canonical/PRINCIPLES.md', compact: 'docs/PRINCIPLES.md', singleton: true,
|
|
62
|
+
soft: 300, hard: 500, quality: 85, visibility: 'internal', template: 'tier0/principles',
|
|
63
|
+
sections: ['Principles', 'Trade-offs', 'Applying these'],
|
|
64
|
+
},
|
|
65
|
+
'constitution.invariants': {
|
|
66
|
+
label: 'Invariants', authority: 'constitution', lens: 'architecture',
|
|
67
|
+
full: 'docs/00-canonical/INVARIANTS.md', compact: 'docs/INVARIANTS.md', singleton: true,
|
|
68
|
+
soft: 400, hard: 800, quality: 95, visibility: 'internal', template: 'tier0/invariants',
|
|
69
|
+
sections: ['Invariants'],
|
|
70
|
+
},
|
|
71
|
+
'constitution.glossary': {
|
|
72
|
+
label: 'Glossary', authority: 'constitution', lens: 'developer',
|
|
73
|
+
full: 'docs/00-canonical/GLOSSARY.md', compact: 'docs/GLOSSARY.md', singleton: true,
|
|
74
|
+
soft: 400, hard: 800, quality: 85, visibility: 'internal', template: 'tier0/glossary',
|
|
75
|
+
sections: ['Terms'],
|
|
76
|
+
},
|
|
77
|
+
'constitution.domains': {
|
|
78
|
+
label: 'Domain Map', authority: 'constitution', lens: 'architecture',
|
|
79
|
+
full: 'docs/00-canonical/DOMAINS.md', compact: 'docs/DOMAINS.md', singleton: true,
|
|
80
|
+
soft: 300, hard: 600, quality: 90, visibility: 'internal', template: 'tier0/domains',
|
|
81
|
+
sections: ['Domains', 'Ownership', 'Boundaries'],
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
'product.vision': {
|
|
85
|
+
label: 'Product Vision', authority: 'requirements', lens: 'architecture',
|
|
86
|
+
full: 'docs/01-product/vision/', compact: 'docs/product/', soft: 400, hard: 700,
|
|
87
|
+
quality: 85, visibility: 'internal', template: 'tier2/vision',
|
|
88
|
+
sections: ['Context', 'Vision', 'Success looks like', 'Non-goals'],
|
|
89
|
+
},
|
|
90
|
+
'product.prd': {
|
|
91
|
+
label: 'PRD', authority: 'requirements', lens: 'developer',
|
|
92
|
+
full: 'docs/01-product/requirements/', compact: 'docs/product/', soft: 800, hard: 1200,
|
|
93
|
+
quality: 90, visibility: 'internal', template: 'tier2/prd',
|
|
94
|
+
sections: ['Context', 'Problem', 'Goals', 'Non-goals', 'Personas', 'Requirements',
|
|
95
|
+
'Functional requirements', 'Non-functional requirements', 'Security considerations',
|
|
96
|
+
'Edge cases', 'Dependencies', 'Acceptance criteria', 'Open questions'],
|
|
97
|
+
},
|
|
98
|
+
'product.feature': {
|
|
99
|
+
label: 'Feature Spec', authority: 'requirements', lens: 'developer',
|
|
100
|
+
full: 'docs/01-product/features/', compact: 'docs/product/', soft: 500, hard: 900,
|
|
101
|
+
quality: 85, visibility: 'internal', template: 'tier2/feature',
|
|
102
|
+
sections: ['Summary', 'Behaviour', 'Requirements', 'Edge cases', 'Acceptance criteria'],
|
|
103
|
+
},
|
|
104
|
+
'product.persona': {
|
|
105
|
+
label: 'Persona', authority: 'requirements', lens: 'user',
|
|
106
|
+
full: 'docs/01-product/personas/', compact: 'docs/product/', soft: 200, hard: 400,
|
|
107
|
+
quality: 80, visibility: 'internal', template: 'tier2/persona',
|
|
108
|
+
sections: ['Who', 'Goals', 'Frustrations', 'Context of use'],
|
|
109
|
+
},
|
|
110
|
+
'product.roadmap': {
|
|
111
|
+
label: 'Roadmap', authority: 'requirements', lens: 'user',
|
|
112
|
+
full: 'docs/01-product/roadmap/', compact: 'docs/product/', soft: 300, hard: 500,
|
|
113
|
+
quality: 75, visibility: 'internal', template: 'tier2/roadmap',
|
|
114
|
+
sections: ['Now', 'Next', 'Later', 'Not planned'],
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
'design.ux': {
|
|
118
|
+
label: 'UX Specification', authority: 'requirements', lens: 'user',
|
|
119
|
+
full: 'docs/02-design/ux/', compact: 'docs/design/', soft: 700, hard: 1000,
|
|
120
|
+
quality: 85, visibility: 'internal', template: 'tier2/ux',
|
|
121
|
+
sections: ['Context', 'Users and tasks', 'Flows', 'States', 'Copy', 'Accessibility', 'Open questions'],
|
|
122
|
+
},
|
|
123
|
+
'design.flow': {
|
|
124
|
+
label: 'User Flow', authority: 'requirements', lens: 'user',
|
|
125
|
+
full: 'docs/02-design/flows/', compact: 'docs/design/', soft: 300, hard: 500,
|
|
126
|
+
quality: 80, visibility: 'internal', template: 'tier2/flow',
|
|
127
|
+
sections: ['Entry points', 'Steps', 'Failure paths', 'Exit states'],
|
|
128
|
+
},
|
|
129
|
+
'design.accessibility': {
|
|
130
|
+
label: 'Accessibility Spec', authority: 'requirements', lens: 'user',
|
|
131
|
+
full: 'docs/02-design/accessibility/', compact: 'docs/design/', soft: 400, hard: 700,
|
|
132
|
+
quality: 85, visibility: 'internal', template: 'tier2/accessibility',
|
|
133
|
+
sections: ['Target level', 'Requirements', 'Known gaps', 'Testing'],
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
'architecture.overview': {
|
|
137
|
+
label: 'Architecture Overview', authority: 'canonical', lens: 'architecture',
|
|
138
|
+
full: 'docs/03-architecture/overview/', compact: 'docs/architecture.md', soft: 400, hard: 700,
|
|
139
|
+
quality: 90, visibility: 'internal', template: 'tier2/arch-overview',
|
|
140
|
+
sections: ['Purpose', 'Context', 'Components', 'Data flows', 'Key decisions', 'Where to go next'],
|
|
141
|
+
},
|
|
142
|
+
'architecture.domain': {
|
|
143
|
+
label: 'Canonical Domain Spec', authority: 'canonical', lens: 'architecture',
|
|
144
|
+
full: 'docs/03-architecture/domains/', compact: 'docs/architecture/', soft: 700, hard: 1000,
|
|
145
|
+
quality: 90, visibility: 'internal', template: 'tier2/domain',
|
|
146
|
+
sections: ['Purpose', 'Boundaries', 'Model', 'Invariants', 'Interfaces',
|
|
147
|
+
'Failure modes', 'Dependencies', 'Open questions'],
|
|
148
|
+
},
|
|
149
|
+
'architecture.component': {
|
|
150
|
+
label: 'Component Spec', authority: 'canonical', lens: 'architecture',
|
|
151
|
+
full: 'docs/03-architecture/components/', compact: 'docs/architecture/', soft: 500, hard: 800,
|
|
152
|
+
quality: 85, visibility: 'internal', template: 'tier2/component',
|
|
153
|
+
sections: ['Responsibility', 'Interfaces', 'Dependencies', 'Failure modes'],
|
|
154
|
+
},
|
|
155
|
+
'architecture.data': {
|
|
156
|
+
label: 'Data Model', authority: 'canonical', lens: 'architecture',
|
|
157
|
+
full: 'docs/03-architecture/data/', compact: 'docs/architecture/', soft: 600, hard: 1000,
|
|
158
|
+
quality: 85, visibility: 'internal', template: 'tier2/data-model',
|
|
159
|
+
sections: ['Entities', 'Relationships', 'Lifecycle', 'Retention', 'Migrations'],
|
|
160
|
+
},
|
|
161
|
+
'architecture.integration': {
|
|
162
|
+
label: 'Integration Spec', authority: 'canonical', lens: 'architecture',
|
|
163
|
+
full: 'docs/03-architecture/integrations/', compact: 'docs/architecture/', soft: 500, hard: 800,
|
|
164
|
+
quality: 85, visibility: 'internal', template: 'tier2/integration',
|
|
165
|
+
sections: ['Counterparty', 'Contract', 'Auth', 'Failure modes', 'Rate limits'],
|
|
166
|
+
},
|
|
167
|
+
'architecture.trd': {
|
|
168
|
+
label: 'TRD', authority: 'requirements', lens: 'developer',
|
|
169
|
+
full: 'docs/03-architecture/components/', compact: 'docs/architecture/', soft: 1000, hard: 1500,
|
|
170
|
+
quality: 90, visibility: 'internal', template: 'tier2/trd',
|
|
171
|
+
sections: ['Context', 'Requirements mapping', 'Existing architecture', 'Proposed architecture',
|
|
172
|
+
'Components', 'Data model', 'Interfaces', 'State transitions', 'Failure handling', 'Security',
|
|
173
|
+
'Performance', 'Observability', 'Migration', 'Testing', 'Rollout', 'Rollback', 'Open questions'],
|
|
174
|
+
},
|
|
175
|
+
'architecture.assessment': {
|
|
176
|
+
label: 'Technical Assessment', authority: 'decision', lens: 'architecture',
|
|
177
|
+
full: 'docs/03-architecture/overview/', compact: 'docs/', soft: 600, hard: 1000,
|
|
178
|
+
quality: 85, visibility: 'internal', template: 'tier2/assessment',
|
|
179
|
+
sections: ['Verdict', 'Findings', 'Risks'],
|
|
180
|
+
},
|
|
181
|
+
'architecture.adr': {
|
|
182
|
+
label: 'ADR', authority: 'decision', lens: 'architecture',
|
|
183
|
+
full: 'docs/03-architecture/adr/', compact: 'docs/adr/', soft: 200, hard: 350,
|
|
184
|
+
quality: 90, visibility: 'internal', template: 'tier2/adr', idPattern: '^adr-\\d{3,4}-',
|
|
185
|
+
sections: ['Status', 'Context', 'Decision', 'Alternatives', 'Consequences', 'Security implications'],
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
'security.architecture': {
|
|
189
|
+
label: 'Security Architecture', authority: 'canonical', lens: 'security',
|
|
190
|
+
full: 'docs/04-security/architecture/', compact: 'docs/security.md', soft: 700, hard: 1000,
|
|
191
|
+
quality: 95, visibility: 'internal', template: 'tier2/security-arch',
|
|
192
|
+
sections: ['Scope', 'Trust boundaries', 'Identity', 'Authorization', 'Secrets',
|
|
193
|
+
'Data protection', 'Assumptions'],
|
|
194
|
+
},
|
|
195
|
+
'security.threat-model': {
|
|
196
|
+
label: 'Threat Model', authority: 'canonical', lens: 'security',
|
|
197
|
+
full: 'docs/04-security/threat-models/', compact: 'docs/security/', soft: 700, hard: 1000,
|
|
198
|
+
quality: 95, visibility: 'internal', template: 'tier2/threat-model',
|
|
199
|
+
sections: ['Scope', 'Assets', 'Actors', 'Trust boundaries', 'Entry points', 'Data flows',
|
|
200
|
+
'Threats', 'Controls', 'Residual risks', 'Assumptions'],
|
|
201
|
+
},
|
|
202
|
+
'security.authorization': {
|
|
203
|
+
label: 'Authorization Model', authority: 'canonical', lens: 'security',
|
|
204
|
+
full: 'docs/04-security/authorization/', compact: 'docs/security/', soft: 500, hard: 800,
|
|
205
|
+
quality: 95, visibility: 'internal', template: 'tier2/authorization',
|
|
206
|
+
sections: ['Subjects', 'Resources', 'Actions', 'Rules', 'Escalation paths'],
|
|
207
|
+
},
|
|
208
|
+
'security.data-classification': {
|
|
209
|
+
label: 'Data Classification', authority: 'canonical', lens: 'security',
|
|
210
|
+
full: 'docs/04-security/data-classification/', compact: 'docs/security/', soft: 400, hard: 700,
|
|
211
|
+
quality: 95, visibility: 'internal', template: 'tier2/data-classification',
|
|
212
|
+
sections: ['Classes', 'Handling rules', 'Storage', 'Retention'],
|
|
213
|
+
},
|
|
214
|
+
'security.public-model': {
|
|
215
|
+
label: 'Public Security Model', authority: 'audience', lens: 'security',
|
|
216
|
+
full: 'docs/11-external/security.md', compact: 'SECURITY.md', soft: 200, hard: 400,
|
|
217
|
+
quality: 90, visibility: 'public', template: 'tier6/security-public',
|
|
218
|
+
sections: ['Reporting a vulnerability', 'Supported versions', 'What we protect', 'Scope'],
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
'engineering.development': {
|
|
222
|
+
label: 'Development Guide', authority: 'audience', lens: 'developer',
|
|
223
|
+
full: 'docs/05-engineering/development/', compact: 'docs/development.md', soft: 500, hard: 800,
|
|
224
|
+
quality: 85, visibility: 'internal', template: 'tier6/development',
|
|
225
|
+
sections: ['Prerequisites', 'Setup', 'Running', 'Testing', 'Troubleshooting'],
|
|
226
|
+
},
|
|
227
|
+
'engineering.testing': {
|
|
228
|
+
label: 'Testing Strategy', authority: 'canonical', lens: 'developer',
|
|
229
|
+
full: 'docs/05-engineering/testing/', compact: 'docs/engineering/', soft: 500, hard: 800,
|
|
230
|
+
quality: 85, visibility: 'internal', template: 'tier2/testing',
|
|
231
|
+
sections: ['Levels', 'What we test', 'What we do not test', 'Tooling', 'Gates'],
|
|
232
|
+
},
|
|
233
|
+
'engineering.conventions': {
|
|
234
|
+
label: 'Conventions', authority: 'canonical', lens: 'developer',
|
|
235
|
+
full: 'docs/05-engineering/conventions/', compact: 'docs/engineering/', soft: 400, hard: 700,
|
|
236
|
+
quality: 80, visibility: 'internal', template: 'tier2/conventions',
|
|
237
|
+
sections: ['Naming', 'Structure', 'Style', 'Review expectations'],
|
|
238
|
+
},
|
|
239
|
+
'engineering.dependencies': {
|
|
240
|
+
label: 'Dependency Policy', authority: 'canonical', lens: 'developer',
|
|
241
|
+
full: 'docs/05-engineering/dependencies/', compact: 'docs/engineering/', soft: 300, hard: 600,
|
|
242
|
+
quality: 80, visibility: 'internal', template: 'tier2/dependencies',
|
|
243
|
+
sections: ['Policy', 'Approved', 'Prohibited', 'Review process'],
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
'operations.deployment': {
|
|
247
|
+
label: 'Deployment Guide', authority: 'canonical', lens: 'operations',
|
|
248
|
+
full: 'docs/06-operations/deployment/', compact: 'docs/operations/', soft: 500, hard: 800,
|
|
249
|
+
quality: 90, visibility: 'internal', template: 'tier2/deployment',
|
|
250
|
+
sections: ['Environments', 'Pipeline', 'Promotion', 'Rollback', 'Verification'],
|
|
251
|
+
},
|
|
252
|
+
'operations.infrastructure': {
|
|
253
|
+
label: 'Infrastructure', authority: 'canonical', lens: 'operations',
|
|
254
|
+
full: 'docs/06-operations/infrastructure/', compact: 'docs/operations/', soft: 600, hard: 1000,
|
|
255
|
+
quality: 85, visibility: 'internal', template: 'tier2/infrastructure',
|
|
256
|
+
sections: ['Topology', 'Components', 'Scaling', 'Cost', 'Access'],
|
|
257
|
+
},
|
|
258
|
+
'operations.configuration': {
|
|
259
|
+
label: 'Configuration Reference', authority: 'generated', lens: 'operations',
|
|
260
|
+
full: 'docs/06-operations/configuration/', compact: 'docs/operations/', soft: 600, hard: 1200,
|
|
261
|
+
quality: 80, visibility: 'internal', template: 'tier3/configuration',
|
|
262
|
+
sections: ['Settings'], generated: true,
|
|
263
|
+
},
|
|
264
|
+
'operations.observability': {
|
|
265
|
+
label: 'Observability', authority: 'canonical', lens: 'operations',
|
|
266
|
+
full: 'docs/06-operations/observability/', compact: 'docs/operations/', soft: 500, hard: 800,
|
|
267
|
+
quality: 85, visibility: 'internal', template: 'tier2/observability',
|
|
268
|
+
sections: ['Signals', 'Dashboards', 'Alerts', 'SLOs', 'On-call'],
|
|
269
|
+
},
|
|
270
|
+
'operations.runbook': {
|
|
271
|
+
label: 'Runbook', authority: 'audience', lens: 'operations',
|
|
272
|
+
full: 'docs/06-operations/runbooks/', compact: 'docs/operations/runbooks/', soft: 400, hard: 700,
|
|
273
|
+
quality: 90, visibility: 'internal', template: 'ops/runbook',
|
|
274
|
+
sections: ['Purpose', 'Trigger', 'Preconditions', 'Diagnostics', 'Procedure',
|
|
275
|
+
'Validation', 'Rollback', 'Escalation'],
|
|
276
|
+
},
|
|
277
|
+
'operations.disaster-recovery': {
|
|
278
|
+
label: 'Disaster Recovery', authority: 'canonical', lens: 'operations',
|
|
279
|
+
full: 'docs/06-operations/disaster-recovery/', compact: 'docs/operations/', soft: 500, hard: 800,
|
|
280
|
+
quality: 95, visibility: 'internal', template: 'ops/disaster-recovery',
|
|
281
|
+
sections: ['Scenarios', 'RPO and RTO', 'Backups', 'Restore procedure', 'Testing'],
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
'release.notes': {
|
|
285
|
+
label: 'Release Notes', authority: 'historical', lens: 'user',
|
|
286
|
+
full: 'docs/07-release/releases/', compact: 'CHANGELOG.md', soft: 400, hard: 1500,
|
|
287
|
+
quality: 75, visibility: 'public', template: 'tier7/release-notes',
|
|
288
|
+
sections: ['Highlights', 'Changes', 'Breaking changes', 'Upgrade notes'],
|
|
289
|
+
},
|
|
290
|
+
'release.changelog': {
|
|
291
|
+
label: 'Changelog', authority: 'historical', lens: 'user',
|
|
292
|
+
full: 'CHANGELOG.md', compact: 'CHANGELOG.md', singleton: true, soft: 500, hard: 2000,
|
|
293
|
+
quality: 75, visibility: 'public', template: 'tier6/changelog',
|
|
294
|
+
// Keep a Changelog structures a changelog by release, not by fixed sections. DocGov
|
|
295
|
+
// adopts that convention rather than competing with it, so the structural gate here is
|
|
296
|
+
// location and size, not section names.
|
|
297
|
+
sections: [],
|
|
298
|
+
},
|
|
299
|
+
'release.migration': {
|
|
300
|
+
label: 'Migration Guide', authority: 'audience', lens: 'user',
|
|
301
|
+
full: 'docs/07-release/migrations/', compact: 'docs/release/', soft: 500, hard: 800,
|
|
302
|
+
quality: 90, visibility: 'public', template: 'tier7/migration',
|
|
303
|
+
sections: ['Who this affects', 'What changed', 'Steps', 'Verification', 'Rollback'],
|
|
304
|
+
},
|
|
305
|
+
'release.deprecation': {
|
|
306
|
+
label: 'Deprecation Notice', authority: 'historical', lens: 'user',
|
|
307
|
+
full: 'docs/07-release/deprecations/', compact: 'docs/release/', soft: 200, hard: 400,
|
|
308
|
+
quality: 85, visibility: 'public', template: 'tier7/deprecation',
|
|
309
|
+
sections: ['What is deprecated', 'Why', 'Timeline', 'Replacement', 'Migration'],
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
'user.readme': {
|
|
313
|
+
label: 'README', authority: 'audience', lens: 'readme',
|
|
314
|
+
full: 'README.md', compact: 'README.md', singleton: true, soft: 300, hard: 500,
|
|
315
|
+
quality: 85, visibility: 'public', template: 'tier6/readme',
|
|
316
|
+
sections: ['Install', 'Usage'],
|
|
317
|
+
},
|
|
318
|
+
'user.getting-started': {
|
|
319
|
+
label: 'Getting Started', authority: 'audience', lens: 'user',
|
|
320
|
+
full: 'docs/08-user/getting-started/', compact: 'docs/user-guide.md', soft: 400, hard: 700,
|
|
321
|
+
quality: 90, visibility: 'public', template: 'tier6/getting-started',
|
|
322
|
+
sections: ['Before you start', 'Install', 'First task', 'What next'],
|
|
323
|
+
},
|
|
324
|
+
'user.guide': {
|
|
325
|
+
label: 'User Guide', authority: 'audience', lens: 'user',
|
|
326
|
+
full: 'docs/08-user/guides/', compact: 'docs/user/', soft: 700, hard: 1000,
|
|
327
|
+
quality: 85, visibility: 'public', template: 'tier6/guide',
|
|
328
|
+
sections: ['Goal', 'Steps', 'Verification', 'Related'],
|
|
329
|
+
},
|
|
330
|
+
'user.admin-guide': {
|
|
331
|
+
label: 'Administrator Guide', authority: 'audience', lens: 'operations',
|
|
332
|
+
full: 'docs/08-user/guides/', compact: 'docs/user/', soft: 700, hard: 1000,
|
|
333
|
+
quality: 85, visibility: 'public', template: 'tier6/admin-guide',
|
|
334
|
+
sections: ['Audience', 'Setup', 'Operations', 'Security', 'Troubleshooting'],
|
|
335
|
+
},
|
|
336
|
+
'user.tutorial': {
|
|
337
|
+
label: 'Tutorial', authority: 'audience', lens: 'user',
|
|
338
|
+
full: 'docs/08-user/guides/', compact: 'docs/user/', soft: 500, hard: 800,
|
|
339
|
+
quality: 85, visibility: 'public', template: 'tier6/tutorial',
|
|
340
|
+
sections: ['What you will build', 'Prerequisites', 'Steps', 'What you learned'],
|
|
341
|
+
},
|
|
342
|
+
'user.troubleshooting': {
|
|
343
|
+
label: 'Troubleshooting', authority: 'audience', lens: 'user',
|
|
344
|
+
full: 'docs/08-user/troubleshooting/', compact: 'docs/user/', soft: 600, hard: 1000,
|
|
345
|
+
quality: 80, visibility: 'public', template: 'tier6/troubleshooting',
|
|
346
|
+
sections: ['Symptoms'],
|
|
347
|
+
},
|
|
348
|
+
'user.faq': {
|
|
349
|
+
label: 'FAQ', authority: 'audience', lens: 'user',
|
|
350
|
+
full: 'docs/08-user/faq/', compact: 'docs/user/', soft: 400, hard: 800,
|
|
351
|
+
quality: 75, visibility: 'public', template: 'tier6/faq',
|
|
352
|
+
sections: ['Questions'],
|
|
353
|
+
},
|
|
354
|
+
'user.reference': {
|
|
355
|
+
label: 'Reference', authority: 'generated', lens: 'developer',
|
|
356
|
+
full: 'docs/08-user/reference/', compact: 'docs/reference/', soft: 1000, hard: 3000,
|
|
357
|
+
quality: 80, visibility: 'public', template: 'tier3/reference', generated: true,
|
|
358
|
+
sections: [],
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
'governance.contributing': {
|
|
362
|
+
label: 'Contributing Guide', authority: 'audience', lens: 'developer',
|
|
363
|
+
full: 'CONTRIBUTING.md', compact: 'CONTRIBUTING.md', singleton: true, soft: 400, hard: 700,
|
|
364
|
+
quality: 85, visibility: 'public', template: 'tier6/contributing',
|
|
365
|
+
sections: ['Before you start', 'Development setup', 'Submitting changes', 'Review process'],
|
|
366
|
+
},
|
|
367
|
+
'governance.code-of-conduct': {
|
|
368
|
+
label: 'Code of Conduct', authority: 'audience', lens: 'user',
|
|
369
|
+
full: 'CODE_OF_CONDUCT.md', compact: 'CODE_OF_CONDUCT.md', singleton: true, soft: 200, hard: 400,
|
|
370
|
+
quality: 80, visibility: 'public', template: 'tier6/code-of-conduct',
|
|
371
|
+
sections: ['What is expected', 'What is not acceptable', 'Scope', 'Reporting'],
|
|
372
|
+
},
|
|
373
|
+
'governance.support': {
|
|
374
|
+
label: 'Support Policy', authority: 'audience', lens: 'user',
|
|
375
|
+
full: 'docs/09-governance/support/', compact: 'SUPPORT.md', soft: 200, hard: 400,
|
|
376
|
+
quality: 80, visibility: 'public', template: 'tier6/support',
|
|
377
|
+
sections: ['Where to get help', 'Response expectations', 'What is out of scope'],
|
|
378
|
+
},
|
|
379
|
+
'governance.lifecycle': {
|
|
380
|
+
label: 'Lifecycle Policy', authority: 'canonical', lens: 'developer',
|
|
381
|
+
full: 'docs/09-governance/lifecycle/', compact: 'docs/governance/', soft: 300, hard: 600,
|
|
382
|
+
quality: 85, visibility: 'public', template: 'tier2/lifecycle',
|
|
383
|
+
sections: ['Versioning', 'Support windows', 'Deprecation policy'],
|
|
384
|
+
},
|
|
385
|
+
'governance.policy': {
|
|
386
|
+
label: 'Policy', authority: 'canonical', lens: 'developer',
|
|
387
|
+
full: 'docs/09-governance/policies/', compact: 'docs/governance/', soft: 400, hard: 700,
|
|
388
|
+
quality: 85, visibility: 'internal', template: 'tier2/policy',
|
|
389
|
+
sections: ['Scope', 'Policy', 'Enforcement', 'Exceptions'],
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
'contract.openapi': {
|
|
393
|
+
label: 'OpenAPI Contract', authority: 'machine-contract', lens: 'developer',
|
|
394
|
+
full: 'openapi/', compact: 'openapi/', soft: 0, hard: 0, quality: 0,
|
|
395
|
+
visibility: 'internal', machine: true, extensions: ['.yaml', '.yml', '.json'], sections: [],
|
|
396
|
+
},
|
|
397
|
+
'contract.schema': {
|
|
398
|
+
label: 'Schema', authority: 'machine-contract', lens: 'developer',
|
|
399
|
+
full: 'schemas/', compact: 'schemas/', soft: 0, hard: 0, quality: 0,
|
|
400
|
+
visibility: 'internal', machine: true, extensions: ['.json', '.yaml', '.proto', '.graphql'], sections: [],
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
'docs.index': {
|
|
404
|
+
label: 'Index', authority: 'audience', lens: 'developer',
|
|
405
|
+
// An index belongs to its directory, wherever that is, so it has no canonical
|
|
406
|
+
// location and `wrong-location` never fires on it.
|
|
407
|
+
full: 'docs/', compact: 'docs/', anywhere: true,
|
|
408
|
+
soft: 200, hard: 400, quality: 75, visibility: 'internal', sections: [],
|
|
409
|
+
},
|
|
410
|
+
'note.internal': {
|
|
411
|
+
label: 'Internal Note', authority: 'historical', lens: 'developer',
|
|
412
|
+
full: 'docs/10-internal/', compact: 'docs/internal/', soft: 400, hard: 1000,
|
|
413
|
+
quality: 70, visibility: 'confidential', template: 'tier7/note',
|
|
414
|
+
sections: [],
|
|
415
|
+
},
|
|
416
|
+
'archive.document': {
|
|
417
|
+
label: 'Archived Document', authority: 'historical', lens: 'developer',
|
|
418
|
+
full: 'docs/99-archive/', compact: 'docs/archive/', soft: 0, hard: 0, quality: 0,
|
|
419
|
+
visibility: 'internal', frozen: true, sections: [],
|
|
420
|
+
},
|
|
421
|
+
'agent.instructions': {
|
|
422
|
+
label: 'Agent Instructions', authority: 'canonical', lens: 'agent',
|
|
423
|
+
full: 'CLAUDE.md', compact: 'CLAUDE.md', soft: 300, hard: 500, quality: 85,
|
|
424
|
+
visibility: 'internal', template: 'tier2/agent-instructions',
|
|
425
|
+
sections: [],
|
|
426
|
+
},
|
|
427
|
+
unknown: {
|
|
428
|
+
label: 'Unclassified', authority: 'historical', lens: 'developer',
|
|
429
|
+
full: 'docs/10-internal/', compact: 'docs/internal/', soft: 400, hard: 800,
|
|
430
|
+
quality: 0, visibility: 'internal', sections: [],
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
/** Namespaces created by `docgov setup --layout full` (PRD §6: may stay empty). */
|
|
435
|
+
export const FULL_NAMESPACES = [
|
|
436
|
+
'docs/00-canonical', 'docs/01-product/vision', 'docs/01-product/requirements',
|
|
437
|
+
'docs/01-product/features', 'docs/01-product/personas', 'docs/01-product/roadmap',
|
|
438
|
+
'docs/02-design/ux', 'docs/02-design/ui', 'docs/02-design/flows', 'docs/02-design/accessibility',
|
|
439
|
+
'docs/03-architecture/overview', 'docs/03-architecture/domains', 'docs/03-architecture/components',
|
|
440
|
+
'docs/03-architecture/data', 'docs/03-architecture/integrations', 'docs/03-architecture/adr',
|
|
441
|
+
'docs/04-security/architecture', 'docs/04-security/threat-models', 'docs/04-security/authorization',
|
|
442
|
+
'docs/04-security/data-classification', 'docs/04-security/security-testing',
|
|
443
|
+
'docs/05-engineering/development', 'docs/05-engineering/testing', 'docs/05-engineering/conventions',
|
|
444
|
+
'docs/05-engineering/dependencies',
|
|
445
|
+
'docs/06-operations/deployment', 'docs/06-operations/infrastructure', 'docs/06-operations/configuration',
|
|
446
|
+
'docs/06-operations/observability', 'docs/06-operations/runbooks', 'docs/06-operations/disaster-recovery',
|
|
447
|
+
'docs/07-release/releases', 'docs/07-release/migrations', 'docs/07-release/deprecations',
|
|
448
|
+
'docs/08-user/getting-started', 'docs/08-user/guides', 'docs/08-user/reference',
|
|
449
|
+
'docs/08-user/troubleshooting', 'docs/08-user/faq',
|
|
450
|
+
'docs/09-governance/contribution', 'docs/09-governance/support', 'docs/09-governance/lifecycle',
|
|
451
|
+
'docs/09-governance/policies',
|
|
452
|
+
'docs/10-internal', 'docs/11-external', 'docs/90-generated', 'docs/99-archive',
|
|
453
|
+
];
|
|
454
|
+
|
|
455
|
+
export const COMPACT_NAMESPACES = ['docs', 'docs/adr'];
|
|
456
|
+
|
|
457
|
+
/** Paths that must only ever contain documents of a given visibility (PRD §10). */
|
|
458
|
+
export const VISIBILITY_PATHS = [
|
|
459
|
+
{ glob: 'docs/10-internal/**', require: ['internal', 'confidential'] },
|
|
460
|
+
{ glob: 'docs/internal/**', require: ['internal', 'confidential'] },
|
|
461
|
+
{ glob: 'docs/11-external/**', require: ['public', 'generated-public'] },
|
|
462
|
+
{ glob: 'docs/public/**', require: ['public', 'generated-public'] },
|
|
463
|
+
{ glob: 'docs/04-security/threat-models/**', require: ['internal', 'confidential'] },
|
|
464
|
+
];
|
|
465
|
+
|
|
466
|
+
/** Generated trees: manual edits are denied, not warned (FEASIBILITY §3.2). */
|
|
467
|
+
export const GENERATED_PATHS = ['docs/90-generated/**', 'docs/generated/**'];
|
|
468
|
+
|
|
469
|
+
export function authorityOf(type) { return (TYPES[type] || TYPES.unknown).authority; }
|
|
470
|
+
export function tierOf(type) { return AUTHORITY[authorityOf(type)].tier; }
|
|
471
|
+
export function typeDef(type) { return TYPES[type] || TYPES.unknown; }
|
|
472
|
+
export function typeIds() { return Object.keys(TYPES); }
|
|
473
|
+
|
|
474
|
+
/** Can `a` legitimately contradict `b`? Only if it is at least as authoritative. */
|
|
475
|
+
export function mayContradict(aType, bType) {
|
|
476
|
+
return AUTHORITY[authorityOf(aType)].rank <= AUTHORITY[authorityOf(bType)].rank;
|
|
477
|
+
}
|