Merge branch 'worktree/ci-native-windows-20260808' into worktree/ci-native-windows-coverage-20260808

This commit is contained in:
Tianyi Cui
2026-08-09 16:47:17 +08:00
376 changed files with 9620 additions and 1108 deletions

View File

@@ -299,22 +299,55 @@ describe('docsPages locale routes', () => {
})
it('publishes the Cordis core API under matching locale structures', () => {
const files = ['context.md', 'events.md', 'fiber.md', 'registry.md', 'service.md', 'inherited.md']
const files = ['context.md', 'events.md', 'fiber.md', 'registry.md', 'service.md']
for (const file of files) {
const root = docsPages.find(page => page.route === `reference/cordis-api/${file}`)
const english = docsPages.find(page => page.route === `en/reference/cordis-api/${file}`)
expect(root?.source).toBe(`docs/cordis-api/${file}`)
expect(root?.source).toBe(`docs/cordis-api/${file.replace(/\.md$/, '.zh.md')}`)
expect(root?.contentLocale).toBe('zh-CN')
expect(root?.section).toBe('Cordis API')
expect(english?.source).toBe(root?.source)
expect(english?.source).toBe(`docs/cordis-api/${file}`)
expect(english?.contentLocale).toBe('en-US')
expect(english?.section).toBe('Cordis Core API')
}
})
it('includes persistence event headings in both locale outlines', () => {
const pages = docsPages.filter(page => page.source === 'docs/persistence-catalog.md')
it('keeps Cordis inherited on the English fallback in both locales', () => {
const pages = docsPages.filter(page => page.route.endsWith('reference/cordis-api/inherited.md'))
expect(pages).toHaveLength(2)
expect(pages.every(page => page.source === 'docs/cordis-api/inherited.md')).toBe(true)
expect(pages.every(page => page.contentLocale === 'en-US')).toBe(true)
})
it('includes persistence event headings in both locale outlines', () => {
const pages = docsPages.filter(page => page.route.endsWith('reference/persistence-catalog.md'))
expect(pages).toHaveLength(2)
expect(pages.map(page => page.source).sort()).toEqual([
'docs/persistence-catalog.md',
'docs/persistence-catalog.zh.md',
])
expect(pages.map(page => page.outline)).toEqual(['deep', 'deep'])
})
it('projects reviewed generated counterparts into root locale routes', () => {
// module-graph, event-producer-consumer, and graph-atlas are paired but intentionally unpublished.
const routes = [
'reference/capability-seams.md',
'reference/agent-lifecycle.md',
'reference/tool-execution-pipeline.md',
'reference/config-catalog.md',
'reference/tool-catalog.md',
'reference/persistence-catalog.md',
'reference/cordis-api/context.md',
'reference/cordis-api/events.md',
'reference/cordis-api/fiber.md',
'reference/cordis-api/registry.md',
'reference/cordis-api/service.md',
]
const pages = routes.map(route => docsPages.find(page => page.route === route))
expect(pages.every(page => page?.contentLocale === 'zh-CN')).toBe(true)
expect(pages.every(page => page?.source.endsWith('.zh.md'))).toBe(true)
})
})
describe('addProjectionFrontmatter', () => {

File diff suppressed because one or more lines are too long

View File

@@ -119,6 +119,12 @@ const otherSource = baseSource.replace('Beta base.', 'Beta other.')
const otherZh = baseZh.replace('乙基础。', '乙对侧。')
const mergedSource = currentSource.replace('Beta base.', 'Beta other.')
const mergedZh = currentZh.replace('乙基础。', '乙对侧。')
const generatedBaseSource = '# Module graph\n\nAlpha base.\n\nBeta base.\n'
const generatedBaseZh = '# 模块图\n\n[English](module-graph.md) | 中文\n\n甲基础。\n\n乙基础。\n'
const generatedCurrentSource = generatedBaseSource.replace('Alpha base.', 'Alpha current.')
const generatedCurrentZh = generatedBaseZh.replace('甲基础。', '甲当前。')
const generatedOtherSource = generatedBaseSource.replace('Beta base.', 'Beta other.')
const generatedOtherZh = generatedBaseZh.replace('乙基础。', '乙对侧。')
const manualBaseSource = baseSource.replace('guide.zh.md', 'manual.zh.md')
const manualBaseZh = baseZh.replace('guide.md', 'manual.md')
const manualCurrentSource = manualBaseSource.replace('Alpha base.', 'Alpha current.')
@@ -257,6 +263,60 @@ describe('translation pairing merge composition', { timeout: 15_000 }, () => {
expect(result.zhHash).toBe(gitBlobHash(Buffer.from(mergedZh)))
})
it('merges a generated source without an English language switcher', () => {
const fixture = createFixture(false)
const ancestor = record(fixture.root, 'docs/module-graph.md', generatedBaseSource, generatedBaseZh)
const current = record(fixture.root, 'docs/module-graph.md', generatedCurrentSource, generatedCurrentZh)
const other = record(fixture.root, 'docs/module-graph.md', generatedOtherSource, generatedOtherZh)
const result = mergeTranslationPairingRecords(
fixture.root,
'docs/module-graph.i18n.yaml',
ancestor,
current,
other,
)
expect(result.sourceContent.toString('utf8')).toBe(
generatedCurrentSource.replace('Beta base.', 'Beta other.'),
)
expect(result.zhContent.toString('utf8')).toBe(generatedCurrentZh.replace('乙基础。', '乙对侧。'))
})
it('rejects an authored source without an English language switcher', () => {
const fixture = createFixture(false)
const source = baseSource.replace('English | [中文](guide.zh.md)\n\n', '')
const ancestor = record(fixture.root, 'docs/guide.md', source, baseZh)
const current = record(fixture.root, 'docs/guide.md', source, baseZh)
const other = record(fixture.root, 'docs/guide.md', source, baseZh)
expect(() => mergeTranslationPairingRecords(
fixture.root,
'docs/guide.i18n.yaml',
ancestor,
current,
other,
)).toThrow('docs/guide.md clean merge lost its language-switcher link to guide.zh.md')
})
it('rejects generated Chinese content without its English backlink', () => {
const fixture = createFixture(false)
const zh = generatedBaseZh.replace('[English](module-graph.md) | 中文\n\n', '')
const ancestor = record(fixture.root, 'docs/module-graph.md', generatedBaseSource, zh)
const current = record(fixture.root, 'docs/module-graph.md', generatedBaseSource, zh)
const other = record(fixture.root, 'docs/module-graph.md', generatedBaseSource, zh)
expect(() => mergeTranslationPairingRecords(
fixture.root,
'docs/module-graph.i18n.yaml',
ancestor,
current,
other,
)).toThrow(
'docs/module-graph.zh.md clean merge lost its language-switcher link to module-graph.md',
)
})
it('leaves owner-content conflicts for a human', () => {
const fixture = createFixture(false)
const ancestor = record(fixture.root, 'docs/guide.md', baseSource, baseZh)

View File

@@ -15,6 +15,7 @@ import {
linksTo,
isTranslationScopeFile,
parseTranslationMarkdown,
requiresSourceLanguageSwitcher,
translationStructureDiff,
translationStructureSignature,
} from './translation-pairing.ts'
@@ -163,7 +164,7 @@ function loadRecordOwners(
function assertMergedPairStructure(paths: TranslationPairPaths, source: Buffer, zh: Buffer): void {
const sourceTree = parseTranslationMarkdown(source.toString('utf8'))
const zhTree = parseTranslationMarkdown(zh.toString('utf8'))
if (!linksTo(sourceTree, basename(paths.zh))) {
if (requiresSourceLanguageSwitcher(paths.source) && !linksTo(sourceTree, basename(paths.zh))) {
throw new Error(`${paths.source} clean merge lost its language-switcher link to ${basename(paths.zh)}`)
}
if (!linksTo(zhTree, basename(paths.source))) {

View File

@@ -4,18 +4,9 @@
".agents/notes/implemented/AGENTS.md",
".agents/notes/implemented/CLAUDE.md",
"docs/AGENTS.md",
"docs/agent-lifecycle.md",
"docs/capability-seams.md",
"docs/config-catalog.md",
"docs/cordis-api/",
"docs/event-producer-consumer.md",
"docs/graph-atlas.md",
"docs/cordis-api/inherited.md",
"docs/i18n/style-samples.md",
"docs/i18n/terminology.md",
"docs/i18n/translation-prompt.md",
"docs/module-graph.md",
"docs/persistence-catalog.md",
"docs/tool-catalog.md",
"docs/tool-execution-pipeline.md"
"docs/i18n/translation-prompt.md"
]
}

View File

@@ -19,6 +19,7 @@ import {
parseTranslationPairingCliArgs,
parseTranslationPairingManifest,
partitionGeneratedRegions,
requiresSourceLanguageSwitcher,
translationStructureDiff,
translationStructureSignature,
} from './translation-pairing.ts'
@@ -142,6 +143,16 @@ describe('translation pairing manifest', () => {
})
})
describe('translation pairing switchers', () => {
it('exempts only paired generated English sources from reciprocal switchers', () => {
expect(requiresSourceLanguageSwitcher('docs/config-catalog.md')).toBe(false)
expect(requiresSourceLanguageSwitcher('docs/cordis-api/context.md')).toBe(false)
expect(requiresSourceLanguageSwitcher('docs/cordis-api/inherited.md')).toBe(false)
expect(requiresSourceLanguageSwitcher('docs/architecture.md')).toBe(true)
expect(requiresSourceLanguageSwitcher('packages/core/session/README.md')).toBe(true)
})
})
describe('translation pairing records', () => {
const paths = translationPairPaths('docs/foo.md')
const record = {

View File

@@ -311,6 +311,28 @@ export function linksTo(tree: Nodes, target: string): boolean {
return found
}
/** Generated English sources cannot carry a switcher without making their generator stale. */
export function requiresSourceLanguageSwitcher(source: string): boolean {
return ![
'docs/agent-lifecycle.md',
'docs/capability-seams.md',
'docs/config-catalog.md',
'docs/cordis-api/context.md',
'docs/cordis-api/events.md',
'docs/cordis-api/fiber.md',
// Excluded from pairing, but kept here for generated-category completeness and direct spec coverage.
'docs/cordis-api/inherited.md',
'docs/cordis-api/registry.md',
'docs/cordis-api/service.md',
'docs/event-producer-consumer.md',
'docs/graph-atlas.md',
'docs/module-graph.md',
'docs/persistence-catalog.md',
'docs/tool-catalog.md',
'docs/tool-execution-pipeline.md',
].includes(source)
}
/** Collect the ordered structural signature, skipping one switcher target. */
export function translationStructureSignature(tree: Nodes, switcherTarget: string): TranslationStructureSignature {
const sig: TranslationStructureSignature = { headings: [], code: [], tables: [], lists: [], links: [] }

View File

@@ -24,6 +24,7 @@ import {
parseTranslationPairingCliArgs,
parseTranslationPairingManifest,
partitionGeneratedRegions,
requiresSourceLanguageSwitcher,
isTranslationScopeFile,
TRANSLATION_SCOPE_GLOB_EXCLUDES,
translationStructureDiff,
@@ -254,7 +255,7 @@ for (const source of [...pairAnchors].sort()) {
if (!linksTo(zhTree, basename(source))) {
errors.push(`${zh}: missing language switcher — no link to ${basename(source)}`)
}
if (!linksTo(sourceTree, basename(zh))) {
if (requiresSourceLanguageSwitcher(source) && !linksTo(sourceTree, basename(zh))) {
errors.push(`${source}: missing language switcher — no link back to ${basename(zh)}`)
}
for (const divergence of translationStructureDiff(