-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathverify-subsystem-pages.ts
More file actions
139 lines (124 loc) · 5.56 KB
/
Copy pathverify-subsystem-pages.ts
File metadata and controls
139 lines (124 loc) · 5.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* Doc-sync gate for package-group subsystem references. Every package group
* either links at least one existing `docs/subsystems/` page from its English
* group README or carries an explicit, justified exemption below.
*/
import { existsSync, globSync, readFileSync } from 'node:fs'
import { resolve, sep } from 'node:path'
import { parseMarkdown, visitMarkdown } from './markdown.ts'
const root = resolve(import.meta.dirname, '..')
/**
* Package groups that do not own a standalone subsystem reference. Reasons
* are reviewable policy: a new group cannot silently inherit an exemption.
*/
export const GROUPS_WITHOUT_SUBSYSTEM_PAGE: Readonly<Record<string, string>> = {
acp: 'Protocol transport entry point; the server package README owns its interoperability contract.',
boot: 'Shared application-bin boot library rather than a runtime subsystem.',
bundle: 'Composition patch carriers whose mounted packages own all runtime contracts.',
hooks: 'External hook-protocol bridges over existing interception points, not a new Harness service.',
sdk: 'Out-of-process protocol and client packages whose package READMEs own the SDK contracts.',
util: 'Low-level primitives whose business semantics remain with their consuming subsystems.',
vision: 'Model-facing image description over an external VLM endpoint; the tool README owns the HTTP, credential, and policy contracts.',
}
/** Result of auditing package-group subsystem documentation. */
export interface SubsystemPageAudit {
/** Package groups discovered from group READMEs or child package manifests. */
readonly groups: number
/** Groups carrying at least one direct subsystem-page link. */
readonly linked: number
/** Groups covered by an explicit no-page policy. */
readonly exempt: number
/** Actionable contract violations. */
readonly violations: readonly string[]
}
/** Normalize one filesystem glob result to repository slash form. */
function normalize(path: string): string {
return path.split(sep).join('/')
}
/** Extract the package-group segment from a repository-relative path. */
function groupOf(path: string): string {
const group = path.split('/')[1]
if (group === undefined || group.length === 0) throw new Error(`invalid package path: ${path}`)
return group
}
/** Return canonical subsystem-page targets linked by one group README. */
function subsystemLinks(source: string): string[] {
const links = new Set<string>()
visitMarkdown(parseMarkdown(source), (node) => {
if (node.type !== 'link') return
const match = /^\.\.\/\.\.\/docs\/subsystems\/([^/#?]+\.md)(?:#[^?#]*)?$/.exec(node.url)
const page = match?.[1]
if (page !== undefined && page !== 'README.md' && !page.endsWith('.zh.md')) links.add(`docs/subsystems/${page}`)
})
return [...links].sort()
}
/**
* Audit package-group subsystem ownership for one repository tree.
* @param scanRoot - repository root containing `packages/` and `docs/`.
* @param exemptions - groups intentionally carrying no subsystem-page link.
* @returns counts plus every actionable violation.
*/
export function auditSubsystemPages(
scanRoot: string = root,
exemptions: Readonly<Record<string, string>> = GROUPS_WITHOUT_SUBSYSTEM_PAGE,
): SubsystemPageAudit {
const readmes = globSync('packages/*/README.md', { cwd: scanRoot }).map(normalize).sort()
const manifests = globSync('packages/*/*/package.json', { cwd: scanRoot }).map(normalize).sort()
const groups = new Set([...readmes, ...manifests].map(groupOf))
const violations: string[] = []
let linked = 0
let exempt = 0
for (const [group, reason] of Object.entries(exemptions)) {
if (!groups.has(group)) {
violations.push(`exemption ${group}: no matching package group; remove the stale entry`)
}
if (reason.trim().length === 0) {
violations.push(`exemption ${group}: missing justification for omitting a subsystem page`)
}
}
for (const group of [...groups].sort()) {
const readme = `packages/${group}/README.md`
const readmePath = resolve(scanRoot, readme)
if (!existsSync(readmePath)) {
violations.push(`${readme}: package group has no group README declaring subsystem ownership`)
continue
}
const links = subsystemLinks(readFileSync(readmePath, 'utf8'))
const isExempt = Object.hasOwn(exemptions, group)
if (links.length === 0) {
if (isExempt) {
exempt += 1
} else {
violations.push(
`${readme}: no reader-visible direct docs/subsystems/*.md link; add the owning page and link,`
+ ' or add a justified GROUPS_WITHOUT_SUBSYSTEM_PAGE entry',
)
}
continue
}
linked += 1
if (isExempt) {
violations.push(`${readme}: links a subsystem page but remains exempt; remove the stale exemption`)
}
for (const page of links) {
if (!existsSync(resolve(scanRoot, page))) {
violations.push(`${readme}: linked subsystem page does not exist: ${page}`)
}
}
}
return { groups: groups.size, linked, exempt, violations }
}
/** Run the repository audit as a standalone doc-sync gate. */
function main(): void {
const audit = auditSubsystemPages()
if (audit.violations.length > 0) {
console.error('verify-subsystem-pages: package-group documentation violations found:')
for (const violation of audit.violations) console.error(` ${violation}`)
process.exit(1)
}
console.log(
`verify-subsystem-pages: ${String(audit.groups)} group(s) checked`
+ ` (${String(audit.linked)} linked, ${String(audit.exempt)} explicitly exempt), all conform.`,
)
}
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) main()