mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
266 lines
13 KiB
TypeScript
266 lines
13 KiB
TypeScript
/**
|
|
* Quick comprehensive documentation-standard tests: the reference example
|
|
* stays valid, the consolidated `dsh-doc` skill carries no stale copied
|
|
* website values or prototype-era language, and the kind system maps each
|
|
* label to exactly one skill template. These run in `pnpm run test` and
|
|
* `pnpm run test:docs` to guard the standard between heavier corpus gates.
|
|
* @module scripts/doc-standard.spec
|
|
*/
|
|
|
|
import { existsSync, globSync, readFileSync } from 'node:fs'
|
|
import { resolve, sep } from 'node:path'
|
|
import { load } from 'js-yaml'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
const root = resolve(import.meta.dirname, '..')
|
|
const PACKAGE_README_GLOBS = [
|
|
'packages/README.md',
|
|
'packages/README.zh.md',
|
|
'packages/*/README.md',
|
|
'packages/*/README.zh.md',
|
|
'packages/*/*/README.md',
|
|
'packages/*/*/README.zh.md',
|
|
] as const
|
|
|
|
function packageReadmes(): string[] {
|
|
return PACKAGE_README_GLOBS
|
|
.flatMap(pattern => globSync(pattern, { cwd: root, exclude: ['**/node_modules/**'] }))
|
|
.map(file => file.replaceAll('\\', '/'))
|
|
.sort()
|
|
}
|
|
|
|
/**
|
|
* The kind system: each label maps to exactly one template in the dsh-doc
|
|
* skill. The check derives the expected kind from the same mechanical facts
|
|
* the skill documents; a kind without a template, a template without a kind,
|
|
* or a document whose kind does not match its position fails here.
|
|
*/
|
|
const KIND_TEMPLATES: Readonly<Record<string, string>> = {
|
|
'package-group': '.agents/skills/dsh-doc/templates/package-group.md',
|
|
'package-reference': '.agents/skills/dsh-doc/templates/package-reference.md',
|
|
'package-library': '.agents/skills/dsh-doc/templates/package-library.md',
|
|
'package-bundle': '.agents/skills/dsh-doc/templates/package-bundle.md',
|
|
}
|
|
|
|
/**
|
|
* Audited packages whose entry is a plain module API rather than a Cordis
|
|
* plugin (`apply` export or a default service export) or an installable
|
|
* bundle (`dsh.bundle.patch`). Each entry names why the package is a
|
|
* library; the check re-derives the entry shape so a stale entry fails loud.
|
|
*/
|
|
const PACKAGE_LIBRARIES: Readonly<Record<string, string>> = {
|
|
'packages/boot/app-boot': 'Boot library the app bins import; plain helper exports.',
|
|
'packages/boot/cmdline': 'Command-line library the app bins import; plain module exports.',
|
|
'packages/client/store': 'Browser-side state primitives; plain function/type exports.',
|
|
'packages/client/ui-primitives': 'Browser-side UI component library; plain component exports.',
|
|
'packages/client/ui-slots': 'Browser-side slot-map declarations; plain type exports.',
|
|
'packages/client/web': 'Browser application boot library; exports the app entry and static module table.',
|
|
'packages/code-runtime/code-runtime-python': 'Host-side protocol library for the CPython subprocess runtime.',
|
|
'packages/core/scope': 'Scoped-context primitives; exports functions and types without a plugin entry.',
|
|
'packages/experimental/webworker-packer': 'Build-time VFS image packer and command library.',
|
|
'packages/experimental/webworker-runtime': 'Browser worker runtime library with explicit host entry points.',
|
|
'packages/hooks/hook-protocol': 'Shared wire-protocol library between the hook bridges.',
|
|
'packages/identity/anonymous-user-id': 'Harness-home identity helper with no plugin registration.',
|
|
'packages/sandbox/sandbox-windows-acl': 'Windows ACL sandbox library consumed by sandbox-local.',
|
|
'packages/sdk/client': 'Client-process library; the spawned runtime owns plugin behavior.',
|
|
'packages/sdk/protocol': 'Wire-protocol library with type declarations only.',
|
|
'packages/session/session-telemetry': 'Telemetry Service Definition and capture library; providers mount the backend.',
|
|
'packages/session/session-title-llm': 'Shared LLM title-provider registration and request policy.',
|
|
'packages/subagent/subagent-in-process-driver': 'Shared one-shot child-agent driver used by provider plugins.',
|
|
'packages/subprocess/win32-process': 'Low-level Win32 process and Job Object primitives.',
|
|
'packages/test-support/session-snapshot': 'Test infrastructure; mounts nothing into a product composition.',
|
|
'packages/test-support/agent-loop-testkit': 'Test helper library; mounts nothing into a product composition.',
|
|
'packages/test-support/client-runtime': 'Browser-side test infrastructure.',
|
|
'packages/test-support/llm-mock-server': 'Test server library; substitutes provider wire behavior.',
|
|
'packages/test-support/loader-smoke': 'Test harness library; mounts nothing into a product composition.',
|
|
'packages/typert/generator': 'Build-time generator run outside any agent runtime.',
|
|
'packages/typert/protocol': 'Compiler-independent protocol declarations.',
|
|
'packages/util/atomic-write': 'Zero-dependency filesystem write utility.',
|
|
'packages/util/brand': 'Type-only branding primitive erased at compile time.',
|
|
'packages/util/crypto': 'Zero-dependency identifier minting utility.',
|
|
'packages/util/home-paths': 'Zero-dependency harness-home path resolver.',
|
|
'packages/util/launch-environment': 'Zero-dependency environment resolver.',
|
|
'packages/util/native-command': 'Host-side subprocess runner utility.',
|
|
'packages/util/output-retention': 'Zero-dependency retention utility.',
|
|
'packages/util/timeout': 'Zero-dependency timeout utility.',
|
|
'packages/util/workspace-path': 'Zero-dependency Workspace path formatter.',
|
|
}
|
|
|
|
function readFrontmatter(file: string): Record<string, unknown> {
|
|
const source = readFileSync(resolve(root, file), 'utf8')
|
|
const match = /^---\n([\s\S]*?)\n---\n/u.exec(source)
|
|
expect(match, `${file}: YAML frontmatter`).not.toBeNull()
|
|
const metadata = load(match?.[1] ?? '')
|
|
expect(metadata, `${file}: frontmatter object`).toBeTypeOf('object')
|
|
expect(Array.isArray(metadata), `${file}: frontmatter object`).toBe(false)
|
|
return metadata as Record<string, unknown>
|
|
}
|
|
|
|
function packageDir(file: string): string {
|
|
return file.replaceAll('\\', '/').replace(/\/README\.zh\.md$/, '').replace(/\/README\.md$/, '')
|
|
}
|
|
|
|
/** Whether the package manifest declares `dsh.bundle.patch`. */
|
|
function declaresBundle(dir: string): boolean {
|
|
const manifest = resolve(root, dir, 'package.json')
|
|
if (!existsSync(manifest)) return false
|
|
const metadata = JSON.parse(readFileSync(manifest, 'utf8')) as { dsh?: { bundle?: { patch?: string } } }
|
|
return metadata.dsh?.bundle?.patch !== undefined
|
|
}
|
|
|
|
/** The expected kind for one package README, from the facts the skill documents. */
|
|
function expectedKind(file: string): string {
|
|
const normalized = file.replaceAll('\\', '/')
|
|
if (normalized.split('/').length <= 3) return 'package-group'
|
|
const dir = packageDir(normalized)
|
|
if (declaresBundle(dir)) return 'package-bundle'
|
|
if (Object.hasOwn(PACKAGE_LIBRARIES, dir)) return 'package-library'
|
|
return 'package-reference'
|
|
}
|
|
|
|
function packageReadmeMetadataErrors(file: string, metadata: Record<string, unknown>): string[] {
|
|
const errors: string[] = []
|
|
if (metadata.kind !== expectedKind(file)) errors.push(`kind must be ${expectedKind(file)}`)
|
|
if (typeof metadata.description !== 'string' || metadata.description.trim() === '') {
|
|
errors.push('description must be a non-empty string')
|
|
}
|
|
for (const field of ['name', 'audience', 'tags', 'i18n']) {
|
|
if (field in metadata) errors.push(`${field} is redundant or has no governed consumer`)
|
|
}
|
|
return errors
|
|
}
|
|
|
|
function packageReadmeStructureErrors(file: string, source: string): string[] {
|
|
const chinese = file.endsWith('.zh.md')
|
|
const required = chinese
|
|
? [[/^## 概述$/m, '概述'], [/^## 目录$/m, '目录'], [/^#{2,3} 开发备注$/m, '开发备注']] as const
|
|
: [[/^## Summary$/m, 'Summary'], [/^## Table of Contents$/m, 'Table of Contents'], [/^#{2,3} Dev Note$/m, 'Dev Note']] as const
|
|
return required.flatMap(([pattern, label]) => pattern.test(source) ? [] : [`missing ${label}`])
|
|
}
|
|
|
|
describe('dsh-doc skill consolidation', () => {
|
|
it('carries no prototype-era language', () => {
|
|
const files = [
|
|
'.agents/skills/dsh-doc/SKILL.md',
|
|
'.agents/skills/dsh-doc/references/metadata-links-i18n.md',
|
|
'.agents/skills/dsh-doc/references/structure-hierarchy.md',
|
|
'.agents/skills/dsh-doc/references/style.md',
|
|
'.agents/skills/dsh-doc/references/review.md',
|
|
'.agents/skills/dsh-doc/references/website-sync.md',
|
|
]
|
|
for (const file of files) {
|
|
const source = readFileSync(resolve(root, file), 'utf8')
|
|
expect(source, file).not.toMatch(/\bprototype\b/i)
|
|
}
|
|
})
|
|
|
|
it('copies no stale website sidebar or section-owner values', () => {
|
|
const source = readFileSync(resolve(root, '.agents/skills/dsh-doc/references/website-sync.md'), 'utf8')
|
|
expect(source).not.toContain('en-docs')
|
|
expect(source).not.toContain('sectionOrder')
|
|
})
|
|
|
|
it('keeps the reference example linked from the skill', () => {
|
|
const skill = readFileSync(resolve(root, '.agents/skills/dsh-doc/SKILL.md'), 'utf8')
|
|
expect(skill).toContain('session-persistence-sqlite/README.md')
|
|
expect(skill).toContain('session-persistence-sqlite/README.zh.md')
|
|
})
|
|
|
|
it('defines controlled English as a precision-preserving review discipline', () => {
|
|
const skill = readFileSync(resolve(root, '.agents/skills/dsh-doc/SKILL.md'), 'utf8')
|
|
const style = readFileSync(resolve(root, '.agents/skills/dsh-doc/references/style.md'), 'utf8')
|
|
expect(skill).toContain('references/style.md#controlled-technical-english')
|
|
expect(style).toContain('not certified ASD-STE100 compliance')
|
|
expect(style).toContain('review prompts, not mechanical gates')
|
|
expect(style).toContain('Never remove or strengthen `must`, `may`, `never`')
|
|
})
|
|
|
|
it('maps every kind label to exactly one skill template that exists', () => {
|
|
const templateFiles = globSync('.agents/skills/dsh-doc/templates/*.md', { cwd: root }).map(path => path.split(sep).join('/')).sort()
|
|
const registered = Object.values(KIND_TEMPLATES).sort()
|
|
expect(templateFiles).toEqual(registered)
|
|
for (const [kind, template] of Object.entries(KIND_TEMPLATES)) {
|
|
expect(existsSync(resolve(root, template)), `${kind}: template ${template}`).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('maps package README kinds to their documentation standards', () => {
|
|
const files = packageReadmes()
|
|
expect(files.length).toBeGreaterThan(0)
|
|
|
|
for (const file of files) {
|
|
const metadata = readFrontmatter(file)
|
|
expect(packageReadmeMetadataErrors(file, metadata), file).toEqual([])
|
|
}
|
|
})
|
|
|
|
it('keeps the audited library registry accurate: every entry has a plain module entry and no bundle declaration', () => {
|
|
for (const [dir, reason] of Object.entries(PACKAGE_LIBRARIES)) {
|
|
expect(reason.trim().length, `${dir}: library justification`).toBeGreaterThan(0)
|
|
expect(declaresBundle(dir), `${dir}: a bundle declaration makes this package-bundle, not a library`).toBe(false)
|
|
const entry = resolve(root, dir, 'src/index.ts')
|
|
expect(existsSync(entry), `${dir}: library entry`).toBe(true)
|
|
const source = readFileSync(entry, 'utf8')
|
|
expect(source, `${dir}: entry must be a plain module, not a plugin`).not.toMatch(/export (?:default|\{[^}]*default[^}]*\} from)/u)
|
|
expect(source, `${dir}: entry must be a plain module, not a plugin`).not.toMatch(/export (?:async )?(?:function|const) apply\b/u)
|
|
}
|
|
})
|
|
|
|
it('keeps every package README on the summary, contents, and Dev Note skeleton', () => {
|
|
for (const file of packageReadmes().filter(file => file.split('/').length === 4)) {
|
|
const source = readFileSync(resolve(root, file), 'utf8')
|
|
expect(packageReadmeStructureErrors(file, source), file).toEqual([])
|
|
}
|
|
})
|
|
|
|
it('rejects redundant fields and a kind that does not match the README position', () => {
|
|
expect(packageReadmeMetadataErrors('packages/example/README.md', {
|
|
description: 'Example group.',
|
|
kind: 'package-reference',
|
|
name: 'example',
|
|
audience: ['developer'],
|
|
tags: ['example'],
|
|
i18n: { counterpart: 'README.zh.md' },
|
|
})).toEqual([
|
|
'kind must be package-group',
|
|
'name is redundant or has no governed consumer',
|
|
'audience is redundant or has no governed consumer',
|
|
'tags is redundant or has no governed consumer',
|
|
'i18n is redundant or has no governed consumer',
|
|
])
|
|
expect(packageReadmeMetadataErrors('packages\\example\\package\\README.md', {
|
|
description: 'Example package.',
|
|
kind: 'package-reference',
|
|
})).toEqual([])
|
|
})
|
|
|
|
it('rejects README-local i18n metadata', () => {
|
|
expect(packageReadmeMetadataErrors('packages\\example\\package\\README.md', {
|
|
description: 'Example package.',
|
|
kind: 'package-reference',
|
|
i18n: {
|
|
'counterpart': 'packages/example/package/README.zh.md',
|
|
'line-aligned': true,
|
|
},
|
|
})).toEqual([
|
|
'i18n is redundant or has no governed consumer',
|
|
])
|
|
})
|
|
})
|
|
|
|
describe('reference-example README pair', () => {
|
|
const dir = 'packages/session/session-persistence-sqlite'
|
|
|
|
it('keeps exact English/Chinese physical line alignment', () => {
|
|
const sourceLines = readFileSync(resolve(root, dir, 'README.md'), 'utf8').split('\n').length
|
|
const zhLines = readFileSync(resolve(root, dir, 'README.zh.md'), 'utf8').split('\n').length
|
|
expect(sourceLines).toBe(zhLines)
|
|
})
|
|
|
|
it('keeps the sidecar consistency record present', () => {
|
|
const sidecar = readFileSync(resolve(root, dir, 'README.i18n.yaml'), 'utf8')
|
|
expect(sidecar).toMatch(/^README\.md: [0-9a-f]{40}$/m)
|
|
expect(sidecar).toMatch(/^README\.zh\.md: [0-9a-f]{40}$/m)
|
|
})
|
|
})
|