docs: enforce bilingual README coverage

This commit is contained in:
Tianyi Cui
2026-07-26 03:29:11 +08:00
parent 0fb392e42f
commit 37bfac749b
12 changed files with 126 additions and 28 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,7 @@
{
"requiredClasses": [
"non-readme"
"non-readme",
"readme"
],
"requiredSince": "2026-07-14",
"required": [

View File

@@ -4,6 +4,7 @@ import { describe, expect, it } from 'vitest'
import {
datedDocumentDate,
isIsoDate,
isTranslationScopeFile,
parseTranslationMarkdown,
parseTranslationPairingManifest,
requiresPairByDate,
@@ -82,6 +83,45 @@ describe('document-class pairing frontier', () => {
expect(requiresTranslationPair('docs/legacy/README.md', manifest)).toBe(true)
expect(requiresTranslationPair('docs/new/README.md', manifest)).toBe(false)
})
it('requires both document classes after the README frontier closes', () => {
const closed = parseTranslationPairingManifest(JSON.stringify({
...manifest,
requiredClasses: ['non-readme', 'readme'],
}))
expect(requiresTranslationPair('docs/guide.md', closed)).toBe(true)
expect(requiresTranslationPair('future/subtree/README.md', closed)).toBe(true)
})
})
describe('translation scope discovery', () => {
it.each([
'README.md',
'apps/cli/README.md',
'future/subtree/readme.md',
'packages/example/README.zh.md',
'native/example/README.i18n.yaml',
'.agents/notes/proposed/feature.md',
'docs/guide.md',
'python/guide.md',
])('includes %s', (file) => {
expect(isTranslationScopeFile(file)).toBe(true)
})
it.each([
'packages/example/guide.md',
'examples/tutorial.md',
'website/reference.md',
'packages/example/README.txt',
'vendor/example/README.md',
'packages/example/node_modules/dependency/README.md',
'packages/example/lib/README.md',
'coverage/report/README.md',
'python/sdk-runtime/src/deepseek_harness_runtime/runtime/dsh-jsonrpc-agent-macos-arm64/README.md',
'python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/README.md',
])('excludes non-source or non-README path %s', (file) => {
expect(isTranslationScopeFile(file)).toBe(false)
})
})
describe('date-based pairing frontier', () => {

View File

@@ -26,6 +26,63 @@ const TRANSLATION_DOCUMENT_CLASSES: TranslationDocumentClass[] = ['readme', 'non
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/
const DATED_DOCUMENT = /(?:^|\/)(\d{4}-\d{2}-\d{2})-[^/]*\.md$/
const README_ARTIFACT = /(?:^|\/)readme(?:\.md|\.zh\.md|\.i18n\.yaml)$/i
const NON_SOURCE_DIRECTORIES = new Set([
'node_modules',
'lib',
'.pnpm-store',
'.cache',
'coverage',
'.sessions',
'.storages',
'tmp',
'dist-exe',
'__pycache__',
'.pytest_cache',
'.artifacts',
'vendor',
])
/** Glob traversal exclusions corresponding to the non-source path predicate. */
export const TRANSLATION_SCOPE_GLOB_EXCLUDES = [
'**/node_modules/**',
'**/lib/**',
'**/.pnpm-store/**',
'**/.cache/**',
'**/coverage/**',
'**/.doc-typecheck-*/**',
'**/.node-next-types-*/**',
'**/.sessions/**',
'**/.storages/**',
'**/tmp/**',
'**/dist-exe/**',
'**/__pycache__/**',
'**/.pytest_cache/**',
'apps/web/dist/**',
'.artifacts/**',
'python/sdk-runtime/src/deepseek_harness_runtime/runtime/dsh-jsonrpc-agent-*/**',
'python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/**',
'vendor/**',
]
/** Whether a repository-relative path belongs to a dependency or generated tree. */
function isTranslationSourceExcluded(file: string): boolean {
const segments = file.split('/')
return segments.some(segment => NON_SOURCE_DIRECTORIES.has(segment)
|| segment.startsWith('.doc-typecheck-')
|| segment.startsWith('.node-next-types-'))
|| file.startsWith('apps/web/dist/')
|| file.startsWith('python/sdk-runtime/src/deepseek_harness_runtime/runtime/dsh-jsonrpc-agent-')
|| file.startsWith('python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/')
}
/** Whether one discovered Markdown or sidecar path belongs to the bilingual source corpus. */
export function isTranslationScopeFile(file: string): boolean {
return !isTranslationSourceExcluded(file) && (README_ARTIFACT.test(file)
|| file.startsWith('.agents/notes/')
|| file.startsWith('docs/')
|| file.startsWith('python/'))
}
/** Whether a string names one real calendar day in canonical ISO form. */
export function isIsoDate(value: string): boolean {

View File

@@ -15,7 +15,9 @@ import {
linksTo,
parseTranslationMarkdown,
parseTranslationPairingManifest,
isTranslationScopeFile,
requiresTranslationPair,
TRANSLATION_SCOPE_GLOB_EXCLUDES,
translationDocumentClass,
translationStructureDiff,
translationStructureSignature,
@@ -25,17 +27,12 @@ const root = resolve(import.meta.dirname, '..')
const listMode = process.argv.includes('--list')
const writeMode = process.argv.includes('--write')
/** Scope of the bilingual contract: root docs, Agent Notes, the docs tree, and the Python SDK tree. */
/** Discover source Markdown and pairing sidecars before applying the corpus predicate. */
const SCOPE_PATTERNS = [
'README.md',
'README.zh.md',
'README.i18n.yaml',
'**/*.md',
'**/*.i18n.yaml',
'.agents/notes/**/*.md',
'.agents/notes/**/*.i18n.yaml',
'docs/**/*.md',
'docs/**/*.i18n.yaml',
'python/**/*.md',
'python/**/*.i18n.yaml',
]
const manifest = parseTranslationPairingManifest(readFileSync(join(root, 'scripts/translation-pairing.manifest.json'), 'utf8'))
@@ -93,7 +90,10 @@ function renderMeta(source: string, sourceHash: string, zh: string, zhHash: stri
// Enumerate the scope once.
const files = new Set<string>()
for (const pattern of SCOPE_PATTERNS) {
for (const match of globSync(pattern, { cwd: root })) files.add(match.split(sep).join('/'))
for (const match of globSync(pattern, { cwd: root, exclude: TRANSLATION_SCOPE_GLOB_EXCLUDES })) {
const normalized = match.split(sep).join('/')
if (isTranslationScopeFile(normalized)) files.add(normalized)
}
}
const translations = [...files].filter(f => f.endsWith('.zh.md')).sort()
const metas = [...files].filter(f => f.endsWith('.i18n.yaml')).sort()