mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge remote-tracking branch 'origin/master' into worktree-tasks-service-seam
# Conflicts: # packages/tasks/README.md # packages/tasks/tasks/README.md # scripts/translation-pairing.manifest.json
This commit is contained in:
62
scripts/clean.spec.ts
Normal file
62
scripts/clean.spec.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
|
||||
import { RepositoryCleaner } from './clean.ts'
|
||||
|
||||
const roots: string[] = []
|
||||
|
||||
function fixture(): string {
|
||||
const root = mkdtempSync(join(tmpdir(), 'dsh-clean-'))
|
||||
roots.push(root)
|
||||
return root
|
||||
}
|
||||
|
||||
function write(path: string, content = ''): void {
|
||||
mkdirSync(dirname(path), { recursive: true })
|
||||
writeFileSync(path, content)
|
||||
}
|
||||
|
||||
function addProject(root: string, path: string): void {
|
||||
write(join(root, 'tsconfig.json'), JSON.stringify({ files: [], references: [{ path }] }))
|
||||
write(join(root, path, 'tsconfig.json'), JSON.stringify({
|
||||
compilerOptions: { composite: true, outDir: 'lib/types' },
|
||||
include: ['src'],
|
||||
}))
|
||||
write(join(root, path, 'src/index.ts'), 'export {}\n')
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
describe('RepositoryCleaner', () => {
|
||||
it('derives live build outputs from project references and removes safe stale package residue', async () => {
|
||||
const root = fixture()
|
||||
addProject(root, 'products/shell')
|
||||
write(join(root, 'products/shell/lib/types/index.js'))
|
||||
write(join(root, 'products/shell/lib/index.js'))
|
||||
write(join(root, '.typecheck/legacy.tsbuildinfo'))
|
||||
write(join(root, 'root.tsbuildinfo'))
|
||||
write(join(root, 'packages/removed/ghost/node_modules/.bin/tool'))
|
||||
|
||||
await new RepositoryCleaner(root).clean()
|
||||
|
||||
expect(existsSync(join(root, 'products/shell/lib'))).toBe(false)
|
||||
expect(existsSync(join(root, 'products/shell/src/index.ts'))).toBe(true)
|
||||
expect(existsSync(join(root, '.typecheck'))).toBe(false)
|
||||
expect(existsSync(join(root, 'root.tsbuildinfo'))).toBe(false)
|
||||
expect(existsSync(join(root, 'packages/removed/ghost'))).toBe(false)
|
||||
})
|
||||
|
||||
it('does not delete any target when a manifest-less package contains an unknown file', async () => {
|
||||
const root = fixture()
|
||||
addProject(root, 'products/shell')
|
||||
write(join(root, 'products/shell/lib/types/index.js'))
|
||||
write(join(root, 'packages/removed/ghost/notes.txt'))
|
||||
|
||||
await expect(new RepositoryCleaner(root).clean()).rejects.toThrow('packages/removed/ghost/notes.txt')
|
||||
expect(existsSync(join(root, 'products/shell/lib'))).toBe(true)
|
||||
})
|
||||
})
|
||||
165
scripts/clean.ts
Normal file
165
scripts/clean.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { lstat, readdir, rm } from 'node:fs/promises'
|
||||
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import ts from 'typescript'
|
||||
import { repositoryConfigHost } from './ts-project.ts'
|
||||
|
||||
const knownOrphanEntries = new Set(['node_modules', 'lib', '.typecheck'])
|
||||
|
||||
function isMissing(error: unknown): boolean {
|
||||
return error instanceof Error && 'code' in error && error.code === 'ENOENT'
|
||||
}
|
||||
|
||||
async function exists(path: string): Promise<boolean> {
|
||||
try {
|
||||
await lstat(path)
|
||||
return true
|
||||
} catch (error) {
|
||||
if (isMissing(error)) return false
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function childDirectories(path: string): Promise<string[]> {
|
||||
try {
|
||||
const entries = await readdir(path, { withFileTypes: true })
|
||||
return entries.filter(entry => entry.isDirectory()).map(entry => join(path, entry.name))
|
||||
} catch (error) {
|
||||
if (isMissing(error)) return []
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
function repositoryPath(root: string, path: string): string {
|
||||
return relative(root, path).split(sep).join('/')
|
||||
}
|
||||
|
||||
function parseConfig(configPath: string): ts.ParsedCommandLine {
|
||||
const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, repositoryConfigHost)
|
||||
if (!parsed) throw new Error(`clean: cannot parse TypeScript config ${configPath}`)
|
||||
if (parsed.errors.length > 0) {
|
||||
throw new Error(parsed.errors.map(error => ts.flattenDiagnosticMessageText(error.messageText, '\n')).join('\n'))
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
/** Plans and removes repository-owned build output without crossing the repository boundary. */
|
||||
export class RepositoryCleaner {
|
||||
constructor(private readonly root: string) {}
|
||||
|
||||
/**
|
||||
* Remove generated build state and package directories containing only known residue.
|
||||
* @returns Repository-relative paths that were removed.
|
||||
*/
|
||||
async clean(): Promise<string[]> {
|
||||
const targets = await this.plan()
|
||||
// Planning validates every target first, so an unsafe orphan prevents all deletion.
|
||||
for (const target of targets) await rm(target, { recursive: true, force: true })
|
||||
return targets.map(target => repositoryPath(this.root, target))
|
||||
}
|
||||
|
||||
private async plan(): Promise<string[]> {
|
||||
const targets = new Set<string>()
|
||||
const unsafeOrphans: string[] = []
|
||||
|
||||
// These checks cover legacy root-level incremental state emitted by older configs.
|
||||
await this.addIfPresent(targets, join(this.root, '.typecheck'))
|
||||
for (const entry of await readdir(this.root, { withFileTypes: true })) {
|
||||
if (entry.isFile() && entry.name.endsWith('.tsbuildinfo')) targets.add(join(this.root, entry.name))
|
||||
}
|
||||
|
||||
// The root project-reference graph is the source of truth for live build targets.
|
||||
// Each emitting project declares lib/types as outDir; its parent lib also owns
|
||||
// the sibling runtime bundles, so the complete build output root is removed.
|
||||
for (const outputDirectory of this.buildOutputDirectories()) {
|
||||
await this.addIfPresent(targets, outputDirectory)
|
||||
}
|
||||
|
||||
for (const groupDirectory of await childDirectories(join(this.root, 'packages'))) {
|
||||
for (const packageDirectory of await childDirectories(groupDirectory)) {
|
||||
// A package.json marks a live package; its output was discovered from the
|
||||
// project graph above, and its package-local node_modules must be preserved.
|
||||
if (await exists(join(packageDirectory, 'package.json'))) {
|
||||
continue
|
||||
}
|
||||
|
||||
// A manifest-less package directory is stale only when every remaining
|
||||
// entry is known generated residue; unknown files make the whole clean fail.
|
||||
const entries = await readdir(packageDirectory)
|
||||
const unknown = entries.filter(entry => !knownOrphanEntries.has(entry) && !entry.endsWith('.tsbuildinfo'))
|
||||
if (unknown.length > 0) {
|
||||
unsafeOrphans.push(...unknown.map(entry => repositoryPath(this.root, join(packageDirectory, entry))))
|
||||
} else {
|
||||
targets.add(packageDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unsafeOrphans.length > 0) {
|
||||
throw new Error([
|
||||
'clean: refusing to remove package directories without package.json; unknown entries remain:',
|
||||
...unsafeOrphans.sort().map(path => ` ${path}`),
|
||||
].join('\n'))
|
||||
}
|
||||
|
||||
return [...targets].sort()
|
||||
}
|
||||
|
||||
private buildOutputDirectories(): string[] {
|
||||
const outputs = new Set<string>()
|
||||
const pending = [join(this.root, 'tsconfig.json')]
|
||||
const visited = new Set<string>()
|
||||
|
||||
while (pending.length > 0) {
|
||||
const nextConfigPath = pending.pop()
|
||||
if (nextConfigPath === undefined) break
|
||||
const configPath = resolve(nextConfigPath)
|
||||
if (visited.has(configPath)) continue
|
||||
visited.add(configPath)
|
||||
|
||||
const parsed = parseConfig(configPath)
|
||||
if (parsed.options.outDir !== undefined) {
|
||||
const typesDirectory = resolve(parsed.options.outDir)
|
||||
if (basename(typesDirectory) !== 'types') {
|
||||
throw new Error(`clean: expected TypeScript outDir to end in /types: ${repositoryPath(this.root, typesDirectory)}`)
|
||||
}
|
||||
const outputDirectory = dirname(typesDirectory)
|
||||
this.assertRepositoryTarget(outputDirectory)
|
||||
outputs.add(outputDirectory)
|
||||
}
|
||||
|
||||
for (const reference of parsed.projectReferences ?? []) {
|
||||
pending.push(ts.resolveProjectReferencePath(reference))
|
||||
}
|
||||
}
|
||||
|
||||
return [...outputs]
|
||||
}
|
||||
|
||||
private assertRepositoryTarget(path: string): void {
|
||||
const repositoryRelative = relative(this.root, path)
|
||||
if (repositoryRelative === '' || repositoryRelative === '..' || repositoryRelative.startsWith(`..${sep}`) || isAbsolute(repositoryRelative)) {
|
||||
throw new Error(`clean: refusing build output outside repository: ${path}`)
|
||||
}
|
||||
}
|
||||
|
||||
private async addIfPresent(targets: Set<string>, path: string): Promise<void> {
|
||||
// Missing outputs are normal on a clean checkout; only existing paths become deletion targets.
|
||||
if (await exists(path)) targets.add(path)
|
||||
}
|
||||
}
|
||||
|
||||
const scriptPath = fileURLToPath(import.meta.url)
|
||||
if (process.argv[1] !== undefined && resolve(process.argv[1]) === scriptPath) {
|
||||
try {
|
||||
const removed = await new RepositoryCleaner(resolve(dirname(scriptPath), '..')).clean()
|
||||
if (removed.length === 0) {
|
||||
console.log('clean: already clean')
|
||||
} else {
|
||||
console.log(`clean: removed ${removed.length} paths`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : error)
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
@@ -7,5 +7,5 @@
|
||||
"docs/testing.md": 1100,
|
||||
"examples/AGENTS.md": 310,
|
||||
"packages/AGENTS.md": 660,
|
||||
"packages/README.md": 790
|
||||
"packages/README.md": 835
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Typecheck Markdown `ts` fences against the workspace API. `ignore-check` fences are reported as
|
||||
* opt-outs; generated catalog fragments and source-equivalence blocks are skipped here because their
|
||||
* owning gates verify them. A build-coordinated mode consumes existing declarations without emit.
|
||||
* owning gates verify them. Byte-identical `.zh.md` copies reuse their unsuffixed sibling's check. A
|
||||
* build-coordinated mode consumes existing declarations without emit.
|
||||
*/
|
||||
|
||||
import { execFileSync } from 'node:child_process'
|
||||
@@ -10,6 +11,7 @@ import { join, relative, resolve } from 'node:path'
|
||||
import ts from 'typescript'
|
||||
import { builtDeclarationPath } from './doc-typecheck-paths.ts'
|
||||
import { extractFences } from './md-fences.ts'
|
||||
import { partitionPairedMarkdownDerivatives } from './paired-markdown-derivatives.ts'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
|
||||
@@ -206,7 +208,12 @@ for (const pattern of markdownGlobs) {
|
||||
}
|
||||
files.sort()
|
||||
|
||||
const all = files.flatMap(extractBlocks)
|
||||
const extracted = files.flatMap(extractBlocks)
|
||||
const { primary: all, derivatives } = partitionPairedMarkdownDerivatives(
|
||||
extracted,
|
||||
block => block.file,
|
||||
block => `${block.kind}\0${block.code}`,
|
||||
)
|
||||
const checked = all.filter(b => b.kind === 'check')
|
||||
const ignored = all.filter(b => b.kind === 'ignore')
|
||||
// Only compile-eligible fences belong in the opt-out ratio; every other skipped
|
||||
@@ -233,7 +240,7 @@ if (compilationError !== undefined) {
|
||||
|
||||
const ratio = ignored.length / ratioDenominator
|
||||
const skipped = all.length - ratioDenominator
|
||||
console.log(`doc-typecheck: ${checked.length} block(s) compiled, ${ignored.length} ignored (${(ratio * 100).toFixed(0)}% opt-out), ${skipped} type-equiv/catalog (checked elsewhere).`)
|
||||
console.log(`doc-typecheck: ${checked.length} block(s) compiled, ${ignored.length} ignored (${(ratio * 100).toFixed(0)}% opt-out), ${skipped} type-equiv/catalog (checked elsewhere), ${derivatives.length} paired derivative(s).`)
|
||||
// Guard against the escape hatch becoming the norm.
|
||||
if (ratioDenominator >= 4 && ratio > 0.5) {
|
||||
console.error(`doc-typecheck: too many blocks opt out of checking (${ignored.length}/${ratioDenominator}). Make them compile or delete them.`)
|
||||
|
||||
66
scripts/paired-markdown-derivatives.spec.ts
Normal file
66
scripts/paired-markdown-derivatives.spec.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { partitionPairedMarkdownDerivatives } from './paired-markdown-derivatives.ts'
|
||||
|
||||
interface Block {
|
||||
doc: string
|
||||
kind: string
|
||||
code: string
|
||||
}
|
||||
|
||||
const partition = (blocks: Block[]) => partitionPairedMarkdownDerivatives(
|
||||
blocks,
|
||||
block => block.doc,
|
||||
block => `${block.kind}\0${block.code}`,
|
||||
)
|
||||
|
||||
describe('partitionPairedMarkdownDerivatives', () => {
|
||||
it('treats a complete byte-identical Chinese sequence as derivative', () => {
|
||||
const english = [
|
||||
{ doc: 'docs/example.md', kind: 'ts', code: 'const one = 1' },
|
||||
{ doc: 'docs/example.md', kind: 'type-equiv', code: 'interface Example {}' },
|
||||
]
|
||||
const chinese = english.map(block => ({ ...block, doc: 'docs/example.zh.md' }))
|
||||
const unrelated = { doc: 'docs/other.md', kind: 'ts', code: 'const other = 2' }
|
||||
|
||||
expect(partition([...english, ...chinese, unrelated])).toEqual({
|
||||
primary: [...english, unrelated],
|
||||
derivatives: chinese,
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps reordered, changed, partial, and orphan Chinese sequences primary', () => {
|
||||
const sequence = (doc: string) => [
|
||||
{ doc, kind: 'ts', code: 'const one = 1' },
|
||||
{ doc, kind: 'ts', code: 'const two = 2' },
|
||||
]
|
||||
const english = sequence('docs/example.md')
|
||||
const changed = english.map((block, index) => ({
|
||||
...block,
|
||||
doc: 'docs/example.zh.md',
|
||||
code: index === 0 ? 'const one = 0' : block.code,
|
||||
}))
|
||||
const reorderedEnglish = sequence('docs/reordered.md')
|
||||
const reordered = [...reorderedEnglish].reverse().map(block => ({ ...block, doc: 'docs/reordered.zh.md' }))
|
||||
const partialEnglish = sequence('docs/partial.md')
|
||||
const partial = [{ ...partialEnglish[0]!, doc: 'docs/partial.zh.md' }]
|
||||
const orphan = [{ doc: 'docs/orphan.zh.md', kind: 'ts', code: 'const orphan = true' }]
|
||||
const blocks = [
|
||||
...english,
|
||||
...changed,
|
||||
...reorderedEnglish,
|
||||
...reordered,
|
||||
...partialEnglish,
|
||||
...partial,
|
||||
...orphan,
|
||||
]
|
||||
|
||||
expect(partition(blocks)).toEqual({ primary: blocks, derivatives: [] })
|
||||
})
|
||||
|
||||
it('requires the fence kind to match as well as the body', () => {
|
||||
const english = { doc: 'docs/example.md', kind: 'type-equiv', code: 'interface Example {}' }
|
||||
const chinese = { ...english, doc: 'docs/example.zh.md', kind: 'public-api' }
|
||||
|
||||
expect(partition([english, chinese])).toEqual({ primary: [english, chinese], derivatives: [] })
|
||||
})
|
||||
})
|
||||
63
scripts/paired-markdown-derivatives.ts
Normal file
63
scripts/paired-markdown-derivatives.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Separate byte-identical Chinese Markdown code blocks from the primary checks
|
||||
* performed on their unsuffixed English siblings. The bilingual pairing gate
|
||||
* owns cross-language identity; source-oriented gates consume one copy.
|
||||
*/
|
||||
|
||||
/** The result of separating canonical blocks from paired Chinese derivatives. */
|
||||
export interface MarkdownDerivativePartition<T> {
|
||||
/** Blocks that still require the caller's owning check. */
|
||||
primary: T[]
|
||||
/** Chinese blocks covered by the byte-identical unsuffixed sequence. */
|
||||
derivatives: T[]
|
||||
}
|
||||
|
||||
/** Return the unsuffixed sibling of a Chinese Markdown path. */
|
||||
function unsuffixedSibling(doc: string): string | null {
|
||||
return doc.endsWith('.zh.md') ? `${doc.slice(0, -'.zh.md'.length)}.md` : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Partition complete byte-identical `.zh.md` block sequences from primary
|
||||
* blocks. A partial or reordered match stays primary so the caller fails
|
||||
* closed; the translation-pairing gate reports the cross-language mismatch.
|
||||
*
|
||||
* @param blocks - Blocks in repository scan order.
|
||||
* @param docOf - Repository-relative Markdown path owning a block.
|
||||
* @param fingerprintOf - Block kind/info string plus byte-exact body.
|
||||
* @returns Primary blocks and paired Chinese derivatives, preserving order.
|
||||
*/
|
||||
export function partitionPairedMarkdownDerivatives<T>(
|
||||
blocks: readonly T[],
|
||||
docOf: (block: T) => string,
|
||||
fingerprintOf: (block: T) => string,
|
||||
): MarkdownDerivativePartition<T> {
|
||||
const byDoc = new Map<string, T[]>()
|
||||
for (const block of blocks) {
|
||||
const doc = docOf(block)
|
||||
const group = byDoc.get(doc)
|
||||
if (group) group.push(block)
|
||||
else byDoc.set(doc, [block])
|
||||
}
|
||||
|
||||
const derivativeDocs = new Set<string>()
|
||||
for (const [doc, candidates] of byDoc) {
|
||||
const sibling = unsuffixedSibling(doc)
|
||||
if (sibling === null) continue
|
||||
const originals = byDoc.get(sibling)
|
||||
if (originals === undefined || originals.length !== candidates.length) continue
|
||||
if (candidates.every((candidate, index) => {
|
||||
const original = originals[index]
|
||||
return original !== undefined && fingerprintOf(candidate) === fingerprintOf(original)
|
||||
})) {
|
||||
derivativeDocs.add(doc)
|
||||
}
|
||||
}
|
||||
|
||||
const primary: T[] = []
|
||||
const derivatives: T[] = []
|
||||
for (const block of blocks) {
|
||||
(derivativeDocs.has(docOf(block)) ? derivatives : primary).push(block)
|
||||
}
|
||||
return { primary, derivatives }
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,216 +1,8 @@
|
||||
{
|
||||
"requiredSince": "2026-07-14",
|
||||
"required": [
|
||||
".agents/notes/README.md",
|
||||
".agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.md",
|
||||
".agents/notes/implemented/architecture/2026-06-11-dev-invariants-over-deep-readonly.md",
|
||||
".agents/notes/implemented/architecture/2026-06-11-event-sourced-sessions.md",
|
||||
".agents/notes/implemented/architecture/2026-06-11-microkernel-event-taxonomy.md",
|
||||
".agents/notes/implemented/architecture/2026-06-11-runtime-arg-validation.md",
|
||||
".agents/notes/implemented/architecture/2026-06-11-structured-error-taxonomy.md",
|
||||
".agents/notes/implemented/architecture/2026-06-11-tool-schemas-in-prompt-assembly.md",
|
||||
".agents/notes/implemented/architecture/2026-06-13-capability-seams.md",
|
||||
".agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md",
|
||||
".agents/notes/implemented/architecture/2026-06-14-session-persistence.md",
|
||||
".agents/notes/implemented/architecture/2026-06-15-turn-enclosure-invariant.md",
|
||||
".agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.md",
|
||||
".agents/notes/implemented/architecture/2026-06-18-agent-lifecycle-and-ownership-seams.md",
|
||||
".agents/notes/implemented/architecture/2026-06-18-session-surface.md",
|
||||
".agents/notes/implemented/architecture/2026-06-18-shared-persistence-write-coordinator.md",
|
||||
".agents/notes/implemented/architecture/2026-06-20-branded-ids.md",
|
||||
".agents/notes/implemented/architecture/2026-06-20-extract-example-app-packages.md",
|
||||
".agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md",
|
||||
".agents/notes/implemented/architecture/2026-06-20-package-hierarchy.md",
|
||||
".agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md",
|
||||
".agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md",
|
||||
".agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.md",
|
||||
".agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md",
|
||||
".agents/notes/implemented/architecture/2026-06-30-event-domain-semantics.md",
|
||||
".agents/notes/implemented/architecture/2026-07-02-fs-per-session-cwd.md",
|
||||
".agents/notes/implemented/architecture/2026-07-02-result-time-applied-hunk-diffs.md",
|
||||
".agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md",
|
||||
".agents/notes/implemented/architecture/2026-07-03-filesystem-directory-listing-seam.md",
|
||||
".agents/notes/implemented/architecture/2026-07-05-prompt-variables-and-tool-guidance-ownership.md",
|
||||
".agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md",
|
||||
".agents/notes/implemented/architecture/2026-07-05-subagent-provider-lifecycle-events.md",
|
||||
".agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md",
|
||||
".agents/notes/implemented/architecture/2026-07-07-tool-call-timeout-policy.md",
|
||||
".agents/notes/implemented/architecture/2026-07-08-agent-scope-contexts.md",
|
||||
".agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md",
|
||||
".agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.md",
|
||||
".agents/notes/implemented/architecture/2026-07-16-explicit-turn-cancellation.md",
|
||||
".agents/notes/implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md",
|
||||
".agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md",
|
||||
".agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md",
|
||||
".agents/notes/implemented/architecture/2026-07-23-client-plugin-loading-model.md",
|
||||
".agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md",
|
||||
".agents/notes/implemented/feature/2026-06-14-acp-multi-session.md",
|
||||
".agents/notes/implemented/feature/2026-06-15-code-mode.md",
|
||||
".agents/notes/implemented/feature/2026-06-17-filesystem-tool-schemas.md",
|
||||
".agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md",
|
||||
".agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md",
|
||||
".agents/notes/implemented/feature/2026-06-22-acp-subagent-backend.md",
|
||||
".agents/notes/implemented/feature/2026-06-25-ask-user-question.md",
|
||||
".agents/notes/implemented/feature/2026-06-29-todo-write-tool.md",
|
||||
".agents/notes/implemented/feature/2026-06-30-hook-bridges.md",
|
||||
".agents/notes/implemented/feature/2026-06-30-hook-protocol-lib.md",
|
||||
".agents/notes/implemented/feature/2026-06-30-interception-seams.md",
|
||||
".agents/notes/implemented/feature/2026-06-30-session-store-fork-api.md",
|
||||
".agents/notes/implemented/feature/2026-06-30-subagent-observe-enrich.md",
|
||||
".agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md",
|
||||
".agents/notes/implemented/feature/2026-07-05-skill-system.md",
|
||||
".agents/notes/implemented/feature/2026-07-06-approval-seam.md",
|
||||
".agents/notes/implemented/feature/2026-07-06-explicit-tool-order.md",
|
||||
".agents/notes/implemented/feature/2026-07-06-sandbox.md",
|
||||
".agents/notes/implemented/feature/2026-07-07-mcp-client-plugin.md",
|
||||
".agents/notes/implemented/feature/2026-07-07-session-prefix.md",
|
||||
".agents/notes/implemented/feature/2026-07-08-repeat-tool-guard.md",
|
||||
".agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md",
|
||||
".agents/notes/implemented/feature/2026-07-10-session-query-service.md",
|
||||
".agents/notes/implemented/feature/2026-07-10-sqlite-session-query-provider.md",
|
||||
".agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.md",
|
||||
".agents/notes/implemented/process/2026-06-11-doc-sync-enforcement.md",
|
||||
".agents/notes/implemented/process/2026-06-11-quality-gates.md",
|
||||
".agents/notes/implemented/process/2026-06-11-tsdown-over-dumble.md",
|
||||
".agents/notes/implemented/process/2026-06-11-vendor-cordis-as-source.md",
|
||||
".agents/notes/implemented/process/2026-06-16-pnpm-over-yarn.md",
|
||||
".agents/notes/implemented/process/2026-06-17-ts-build-config.md",
|
||||
".agents/notes/implemented/process/2026-06-18-markdown-cross-link-lint.md",
|
||||
".agents/notes/implemented/process/2026-06-20-agent-note-classification.md",
|
||||
".agents/notes/implemented/process/2026-06-20-core-data-structures-catalog.md",
|
||||
".agents/notes/implemented/process/2026-06-20-generated-cordis-catalog.md",
|
||||
".agents/notes/implemented/process/2026-07-02-bilingual-docs-and-pairing-gate.md",
|
||||
".agents/notes/implemented/process/2026-07-02-tool-schema-catalog.md",
|
||||
".agents/notes/implemented/process/2026-07-03-documentation-graph-atlas.md",
|
||||
".agents/notes/implemented/process/2026-07-04-cordis-jsdoc-completeness-gate.md",
|
||||
".agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md",
|
||||
".agents/notes/implemented/process/2026-07-04-persistence-log-catalog.md",
|
||||
".agents/notes/implemented/process/2026-07-05-uniform-agent-note-format.md",
|
||||
".agents/notes/implemented/process/2026-07-06-export-surface-jsdoc-gate.md",
|
||||
".agents/notes/implemented/process/2026-07-06-generated-config-catalog.md",
|
||||
".agents/notes/implemented/process/2026-07-06-node-engine-floor.md",
|
||||
".agents/notes/implemented/process/2026-07-06-parallel-pre-push-gates.md",
|
||||
".agents/notes/implemented/process/2026-07-10-readme-known-limitations-gate.md",
|
||||
".agents/notes/implemented/process/2026-07-12-package-model-experience-contract.md",
|
||||
".agents/notes/implemented/process/2026-07-19-web-styling-system.md",
|
||||
".agents/notes/implemented/simplification/2026-06-19-drop-mutable-session-summary.md",
|
||||
".agents/notes/implemented/simplification/2026-06-20-collapse-trace-only-session-events.md",
|
||||
".agents/notes/implemented/simplification/2026-06-20-drop-unconsumed-llm-adapter-change-event.md",
|
||||
".agents/notes/implemented/simplification/2026-06-20-drop-unconsumed-llm-assembled-surfaces.md",
|
||||
".agents/notes/implemented/simplification/2026-06-20-prune-dead-seam-methods.md",
|
||||
".agents/notes/implemented/simplification/2026-06-20-public-agent-stop-surface.md",
|
||||
".agents/notes/implemented/simplification/2026-06-20-remove-agent-boundary-mirror-events.md",
|
||||
".agents/notes/implemented/simplification/2026-06-20-unify-agent-and-session-id.md",
|
||||
".agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md",
|
||||
".agents/notes/implemented/simplification/2026-07-02-remove-stream-chunk-mirror.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-drop-image-content-block.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-drop-inert-request-knobs.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-prune-producerless-vocabulary-variants.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-prune-write-only-fs-surface.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-remove-agent-steering-mirror.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-share-app-bin-boot-glue.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-tighten-hook-protocol-contract.md",
|
||||
".agents/notes/implemented/simplification/2026-07-04-trim-acp-bridge-unreachable-surface.md",
|
||||
".agents/notes/implemented/simplification/2026-07-12-drop-unconsumed-skill-provider-events.md",
|
||||
".agents/notes/implemented/simplification/2026-07-12-prune-unused-web-seam-fields.md",
|
||||
".agents/notes/implemented/simplification/2026-07-12-simplify-session-log-representation.md",
|
||||
".agents/notes/implemented/testing/2026-06-11-property-based-testing.md",
|
||||
".agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md",
|
||||
".agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md",
|
||||
".agents/notes/implemented/testing/2026-06-20-remove-redundant-snapshot-log-expected-output.md",
|
||||
".agents/notes/implemented/testing/2026-06-22-fork-child-replay-seed-boundary.md",
|
||||
".agents/notes/implemented/testing/2026-06-22-fork-snapshot-scenarios.md",
|
||||
".agents/notes/implemented/testing/2026-06-22-subagent-snapshot-replay.md",
|
||||
".agents/notes/implemented/testing/2026-07-04-hook-snapshot-matrix.md",
|
||||
".agents/notes/implemented/testing/2026-07-04-single-source-acp-replay-config.md",
|
||||
".agents/notes/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.md",
|
||||
".agents/notes/implemented/testing/2026-07-08-shared-acp-snapshot-package.md",
|
||||
".agents/notes/proposed/architecture/2026-06-16-typed-event-schemas.md",
|
||||
".agents/notes/proposed/feature/2026-06-30-pre-tool-input-rewrite.md",
|
||||
".agents/notes/proposed/feature/2026-07-07-claude-code-and-codex-subagent-backends.md",
|
||||
".agents/notes/proposed/feature/2026-07-08-interactive-side-sessions.md",
|
||||
".agents/notes/proposed/process/2026-06-11-api-extractor-reports.md",
|
||||
".agents/notes/proposed/process/2026-06-11-architectural-conformance.md",
|
||||
".agents/notes/proposed/process/2026-06-11-supply-chain-and-vendor-drift.md",
|
||||
".agents/notes/proposed/process/2026-06-20-discover-package-inventory.md",
|
||||
".agents/notes/proposed/simplification/2026-07-04-prune-dead-core-spine-surface.md",
|
||||
".agents/notes/proposed/testing/2026-06-11-deterministic-and-stress-testing.md",
|
||||
".agents/notes/proposed/testing/2026-06-11-mutation-testing.md",
|
||||
".agents/notes/rejected/architecture/2026-06-11-immutable-public-surfaces.md",
|
||||
".agents/notes/rejected/architecture/2026-06-20-providerless-example-base.md",
|
||||
".agents/notes/rejected/feature/2026-07-13-stream-workflow-progress-through-tool-calls.md",
|
||||
".agents/notes/rejected/process/2026-07-04-generate-agent-note-index-tables.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-assembled-assistant-messages-only.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-drop-acp-session-load.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-drop-acp-terminal-meta.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-drop-bash-output-spill-files.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-drop-durable-step-boundaries.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-drop-unused-session-lineage.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-fold-session-persistence-interface.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-generic-tool-rendering.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-retire-mid-turn-steering.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-single-session-acp-bridge.md",
|
||||
".agents/notes/rejected/simplification/2026-06-20-truncate-interrupted-turns.md",
|
||||
".agents/notes/rejected/simplification/2026-07-04-prune-unimplemented-subagent-vocabulary.md",
|
||||
".agents/notes/rejected/simplification/2026-07-12-collapse-workflow-to-foreground-core.md",
|
||||
".agents/notes/rejected/simplification/2026-07-12-prune-unused-skill-registry-surface.md",
|
||||
"README.md",
|
||||
"docs/architecture.md",
|
||||
"docs/cookbook/adding-a-package.md",
|
||||
"docs/cookbook/adding-a-tool.md",
|
||||
"docs/cookbook/adding-a-vendored-package.md",
|
||||
"docs/cookbook/adding-an-llm-adapter.md",
|
||||
"docs/cookbook/extension-cookbook.md",
|
||||
"docs/cookbook/responding-to-pr-review-on-a-stack.md",
|
||||
"docs/cordis-primer.md",
|
||||
"docs/core-data-structures/approval.md",
|
||||
"docs/core-data-structures/bash.md",
|
||||
"docs/core-data-structures/code-runtime.md",
|
||||
"docs/core-data-structures/compaction.md",
|
||||
"docs/core-data-structures/core.md",
|
||||
"docs/core-data-structures/filesystem.md",
|
||||
"docs/core-data-structures/llm-streaming.md",
|
||||
"docs/core-data-structures/persistence.md",
|
||||
"docs/core-data-structures/sandbox.md",
|
||||
"docs/core-data-structures/scope.md",
|
||||
"docs/core-data-structures/session-query.md",
|
||||
"docs/core-data-structures/session.md",
|
||||
"docs/core-data-structures/skills.md",
|
||||
"docs/core-data-structures/subagent.md",
|
||||
"docs/core-data-structures/system-prompt.md",
|
||||
"docs/core-data-structures/tools.md",
|
||||
"docs/core-data-structures/user-interaction.md",
|
||||
"docs/core-data-structures/web.md",
|
||||
"docs/core-data-structures/workflow.md",
|
||||
"docs/defensive-patterns.md",
|
||||
"docs/development.md",
|
||||
"docs/glossary.md",
|
||||
"docs/i18n/README.md",
|
||||
"docs/i18n/translation-rules.md",
|
||||
"docs/postmortem/0001-acp-default-export-drops-inject.md",
|
||||
"docs/postmortem/0002-js-expression-disabled-filesystem-tools.md",
|
||||
"docs/postmortem/README.md",
|
||||
"docs/testing.md",
|
||||
"docs/user/develop/basic/config.md",
|
||||
"docs/user/develop/basic/index.md",
|
||||
"docs/user/develop/basic/tool.md",
|
||||
"docs/user/develop/framework/events.md",
|
||||
"docs/user/develop/framework/index.md",
|
||||
"docs/user/develop/framework/service.md",
|
||||
"docs/user/develop/practice/index.md",
|
||||
"docs/user/develop/practice/llm-adapter.md",
|
||||
"docs/user/guide/config.md",
|
||||
"docs/user/guide/index.md",
|
||||
"docs/user/guide/quickstart.md",
|
||||
"docs/user/index.md",
|
||||
"python/README.md",
|
||||
"python/sdk-runtime/README.md",
|
||||
"python/sdk/README.md"
|
||||
],
|
||||
"excluded": [
|
||||
".agents/notes/AGENTS.md",
|
||||
".agents/notes/implemented/AGENTS.md",
|
||||
".agents/notes/implemented/CLAUDE.md",
|
||||
"docs/AGENTS.md",
|
||||
"docs/agent-lifecycle.md",
|
||||
"docs/capability-seams.md",
|
||||
@@ -224,7 +16,6 @@
|
||||
"docs/module-graph.md",
|
||||
"docs/persistence-catalog.md",
|
||||
"docs/tool-catalog.md",
|
||||
"docs/tool-execution-pipeline.md",
|
||||
"python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/"
|
||||
"docs/tool-execution-pipeline.md"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/** Regression tests for the bilingual cutoff and structural signature. */
|
||||
/** Regression tests for the bilingual corpus scope and structural signature. */
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
datedDocumentDate,
|
||||
isIsoDate,
|
||||
isTranslationScopeFile,
|
||||
parseTranslationMarkdown,
|
||||
parseTranslationPairingManifest,
|
||||
requiresPairByDate,
|
||||
translationStructureDiff,
|
||||
translationStructureSignature,
|
||||
} from './translation-pairing.ts'
|
||||
@@ -16,49 +14,60 @@ function signature(markdown: string) {
|
||||
}
|
||||
|
||||
describe('translation pairing manifest', () => {
|
||||
it('accepts a real ISO cutoff and string-array fields', () => {
|
||||
it('accepts an exclusions-only manifest', () => {
|
||||
expect(parseTranslationPairingManifest(JSON.stringify({
|
||||
requiredSince: '2026-07-14',
|
||||
required: ['README.md'],
|
||||
excluded: ['docs/generated/'],
|
||||
}))).toEqual({
|
||||
requiredSince: '2026-07-14',
|
||||
required: ['README.md'],
|
||||
excluded: ['docs/generated/'],
|
||||
})
|
||||
})
|
||||
|
||||
it.each(['2026-7-14', '2026-02-29', '2026-13-01', 'not-a-date'])('rejects invalid cutoff %s', (cutoff) => {
|
||||
expect(isIsoDate(cutoff)).toBe(false)
|
||||
it.each([
|
||||
['required', ['packages/README.md']],
|
||||
['requiredClasses', ['readme']],
|
||||
['requiredSince', '2026-07-14'],
|
||||
] as const)('rejects obsolete policy field %s instead of accepting an inert requirement', (field, value) => {
|
||||
expect(() => parseTranslationPairingManifest(JSON.stringify({
|
||||
requiredSince: cutoff,
|
||||
required: [],
|
||||
excluded: [],
|
||||
}))).toThrow('requiredSince must be a valid YYYY-MM-DD date')
|
||||
[field]: value,
|
||||
}))).toThrow(`unsupported field(s): ${field}; every in-scope document is required`)
|
||||
})
|
||||
|
||||
it('rejects non-string manifest arrays', () => {
|
||||
it('rejects a missing or non-string exclusion list', () => {
|
||||
expect(() => parseTranslationPairingManifest('{}')).toThrow('excluded must be an array of strings')
|
||||
expect(() => parseTranslationPairingManifest(JSON.stringify({
|
||||
requiredSince: '2026-07-14',
|
||||
required: [42],
|
||||
excluded: [],
|
||||
}))).toThrow('required must be an array of strings')
|
||||
excluded: [42],
|
||||
}))).toThrow('excluded must be an array of strings')
|
||||
})
|
||||
})
|
||||
|
||||
describe('date-based pairing frontier', () => {
|
||||
const cutoff = '2026-07-14'
|
||||
|
||||
it('enforces the cutoff day and every later day, but not the preceding day', () => {
|
||||
expect(requiresPairByDate('.agents/notes/2026-07-13-before.md', cutoff)).toBe(false)
|
||||
expect(requiresPairByDate('.agents/notes/2026-07-14-at-cutoff.md', cutoff)).toBe(true)
|
||||
expect(requiresPairByDate('.agents/notes/2026-07-15-after.md', cutoff)).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('matches only a date at the start of the basename', () => {
|
||||
expect(datedDocumentDate('.agents/notes/2026-07-14-proposal.md')).toBe('2026-07-14')
|
||||
expect(datedDocumentDate('docs/release-notes-2026-07-14-alpha.md')).toBeUndefined()
|
||||
expect(requiresPairByDate('docs/release-notes-2026-07-14-alpha.md', cutoff)).toBe(false)
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Pure parsing and structural helpers for the bilingual-document pairing
|
||||
* gate. Kept separate from the CLI so cutoff and signature behavior can be
|
||||
* regression-tested without reading or mutating the repository tree.
|
||||
* gate. Kept separate from the CLI so corpus discovery and signature behavior
|
||||
* can be regression-tested without reading or mutating the repository tree.
|
||||
*/
|
||||
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown'
|
||||
@@ -11,31 +11,77 @@ import type { Nodes } from 'mdast'
|
||||
|
||||
/** Validated shape of `scripts/translation-pairing.manifest.json`. */
|
||||
export interface TranslationPairingManifest {
|
||||
required: string[]
|
||||
/** Source documents exempt from pairing because they are generated, instructional, or bilingual by construction. */
|
||||
excluded: string[]
|
||||
/** Date-named documents on or after this day must merge bilingual. */
|
||||
requiredSince: string
|
||||
}
|
||||
|
||||
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',
|
||||
])
|
||||
|
||||
/** Whether a string names one real calendar day in canonical ISO form. */
|
||||
export function isIsoDate(value: string): boolean {
|
||||
if (!ISO_DATE.test(value)) return false
|
||||
const date = new Date(`${value}T00:00:00.000Z`)
|
||||
return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value
|
||||
/** 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/')
|
||||
}
|
||||
|
||||
/** Read one manifest string-array field or fail before enforcement starts. */
|
||||
function stringArrayField(record: Record<string, unknown>, field: 'required' | 'excluded'): string[] {
|
||||
const value = record[field]
|
||||
/** 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/'))
|
||||
}
|
||||
|
||||
/** Read the manifest exclusion list or fail before enforcement starts. */
|
||||
function excludedField(record: Record<string, unknown>): string[] {
|
||||
const value = record.excluded
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error(`translation-pairing.manifest.json: ${field} must be an array of strings`)
|
||||
throw new Error('translation-pairing.manifest.json: excluded must be an array of strings')
|
||||
}
|
||||
const entries: unknown[] = value
|
||||
if (!entries.every((entry): entry is string => typeof entry === 'string')) {
|
||||
throw new Error(`translation-pairing.manifest.json: ${field} must be an array of strings`)
|
||||
throw new Error('translation-pairing.manifest.json: excluded must be an array of strings')
|
||||
}
|
||||
return entries
|
||||
}
|
||||
@@ -47,26 +93,11 @@ export function parseTranslationPairingManifest(content: string): TranslationPai
|
||||
throw new Error('translation-pairing.manifest.json: expected an object')
|
||||
}
|
||||
const record = value as Record<string, unknown>
|
||||
const requiredSince = record.requiredSince
|
||||
if (typeof requiredSince !== 'string' || !isIsoDate(requiredSince)) {
|
||||
throw new Error(`translation-pairing.manifest.json: requiredSince must be a valid YYYY-MM-DD date; got ${JSON.stringify(requiredSince)}`)
|
||||
const unsupported = Object.keys(record).filter(field => field !== 'excluded')
|
||||
if (unsupported.length > 0) {
|
||||
throw new Error(`translation-pairing.manifest.json: unsupported field(s): ${unsupported.join(', ')}; every in-scope document is required`)
|
||||
}
|
||||
return {
|
||||
required: stringArrayField(record, 'required'),
|
||||
excluded: stringArrayField(record, 'excluded'),
|
||||
requiredSince,
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the leading date of a `yyyy-mm-dd-*.md` basename, if present. */
|
||||
export function datedDocumentDate(file: string): string | undefined {
|
||||
return DATED_DOCUMENT.exec(file)?.[1]
|
||||
}
|
||||
|
||||
/** Whether a date-named document falls on or after the pairing cutoff. */
|
||||
export function requiresPairByDate(file: string, requiredSince: string): boolean {
|
||||
const date = datedDocumentDate(file)
|
||||
return date !== undefined && date >= requiredSince
|
||||
return { excluded: excludedField(record) }
|
||||
}
|
||||
|
||||
/** The structural surface compared between the two sides of a pair. */
|
||||
|
||||
@@ -11,7 +11,8 @@ interface ProjectGraph {
|
||||
options: ts.CompilerOptions
|
||||
}
|
||||
|
||||
const configHost: ts.ParseConfigFileHost = {
|
||||
/** TypeScript config host shared by repository scripts. */
|
||||
export const repositoryConfigHost: ts.ParseConfigFileHost = {
|
||||
useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
|
||||
readDirectory: (...args) => ts.sys.readDirectory(...args),
|
||||
fileExists: fileName => ts.sys.fileExists(fileName),
|
||||
@@ -52,7 +53,7 @@ function loadProjectGraph(projectRoot: string): ProjectGraph {
|
||||
|
||||
/** Parse one config file and fail loud on any config diagnostic. */
|
||||
function parseConfig(configPath: string): ts.ParsedCommandLine {
|
||||
const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, configHost)
|
||||
const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, repositoryConfigHost)
|
||||
if (!parsed) throw new Error(`cannot parse TypeScript config ${configPath}`)
|
||||
if (parsed.errors.length > 0) {
|
||||
throw new Error(parsed.errors.map(error => ts.flattenDiagnosticMessageText(error.messageText, '\n')).join('\n'))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"comment": "Maps each ` ```ts type-equiv ` or ` ```ts public-api ` block (by doc + declared symbol + projection) to the source declaration and original JSDoc it must match. Omit projection for the complete declaration; use public-api with a ` ```ts public-api ` block for a body-stripped public class declaration. verify-type-equiv.ts enforces a 1:1 correspondence: every source-equivalence block has exactly one entry here, and every entry resolves to exactly one block. Add an entry when you add a source-equivalence block; remove it when you remove the block.",
|
||||
"comment": "Maps each primary ` ```ts type-equiv ` or ` ```ts public-api ` block (by doc + declared symbol + projection) to the source declaration and original JSDoc it must match. Paired `.zh.md` blocks are byte-identical derivatives checked through their unsuffixed sibling and have no duplicate entry. Omit projection for the complete declaration; use public-api with a ` ```ts public-api ` block for a body-stripped public class declaration. verify-type-equiv.ts enforces a 1:1 correspondence between primary blocks and entries. Add an entry when you add a primary source-equivalence block; remove it when you remove the block.",
|
||||
"entries": [
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.md",
|
||||
@@ -1248,949 +1248,6 @@
|
||||
"doc": "docs/core-data-structures/session-query.md",
|
||||
"symbol": "SessionSearchHit",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "Branded",
|
||||
"source": "packages/util/brand/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "ContentBlockMap",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "AssistantProvenance",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "Message",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "MessageSourceMap",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "FinishReasonMap",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "LlmProviderInfo",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "LlmModelInfo",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "LlmModelContext",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "GenerateOptions",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "ToolSchema",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "LlmCallConfig",
|
||||
"source": "packages/llm/llm/src/call-config.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "SessionEvent",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "SendOptions",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "AgentCancelCause",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "InjectOptions",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "ResolvedAgentInput",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "AgentMessageId",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "AgentMessage",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "CancelOptions",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "Agent",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "HookContext",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "PromptDecision",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "ContinuationDecision",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "RequestError",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "RequestErrorDecision",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "ContinuationStop",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/core.zh.md",
|
||||
"symbol": "SessionStartSource",
|
||||
"source": "packages/core/agent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/scope.zh.md",
|
||||
"symbol": "ScopeKey",
|
||||
"source": "packages/core/scope/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/scope.zh.md",
|
||||
"symbol": "Scoped",
|
||||
"source": "packages/core/scope/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/scope.zh.md",
|
||||
"symbol": "Scope",
|
||||
"source": "packages/core/scope/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/scope.zh.md",
|
||||
"symbol": "ScopeLayer",
|
||||
"source": "packages/core/scope/src/store.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/system-prompt.zh.md",
|
||||
"symbol": "AssembleContext",
|
||||
"source": "packages/core/system-prompt/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/system-prompt.zh.md",
|
||||
"symbol": "PromptSection",
|
||||
"source": "packages/core/system-prompt/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/system-prompt.zh.md",
|
||||
"symbol": "ToolProviderResult",
|
||||
"source": "packages/core/system-prompt/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/llm-streaming.zh.md",
|
||||
"symbol": "StreamChunk",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/llm-streaming.zh.md",
|
||||
"symbol": "LlmFailure",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/llm-streaming.zh.md",
|
||||
"symbol": "TokenUsage",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/llm-streaming.zh.md",
|
||||
"symbol": "ContentBlockMap",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/llm-streaming.zh.md",
|
||||
"symbol": "AppIdentity",
|
||||
"source": "packages/llm/llm/src/attribution.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/llm-streaming.zh.md",
|
||||
"symbol": "BlockAssembler",
|
||||
"source": "packages/llm/llm/src/assembler.ts",
|
||||
"projection": "public-api"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/llm-streaming.zh.md",
|
||||
"symbol": "LlmAdapter",
|
||||
"source": "packages/llm/llm/src/index.ts",
|
||||
"projection": "public-api"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "PromptMessageData",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "SessionEventMap",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "OutOfBandSessionEventMap",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "EpochHeader",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "TodoItem",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "SessionEvent",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "TurnTriggerMap",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "TurnEndReasonMap",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "SurfaceEventType",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "SurfaceOp",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "SurfaceIntent",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "SessionSurface",
|
||||
"source": "packages/core/session/src/surface.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "SurfaceFoldReplacement",
|
||||
"source": "packages/core/session/src/surface.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "SurfaceFoldResult",
|
||||
"source": "packages/core/session/src/surface.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session.zh.md",
|
||||
"symbol": "Session",
|
||||
"source": "packages/core/session/src/index.ts",
|
||||
"projection": "public-api"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/persistence.zh.md",
|
||||
"symbol": "SessionHeader",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/persistence.zh.md",
|
||||
"symbol": "CreateSessionOptions",
|
||||
"source": "packages/core/session/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/persistence.zh.md",
|
||||
"symbol": "SessionLocation",
|
||||
"source": "packages/session-persistence/session-persistence/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/persistence.zh.md",
|
||||
"symbol": "SessionPersistenceRevision",
|
||||
"source": "packages/session-persistence/session-persistence/src/revision.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/persistence.zh.md",
|
||||
"symbol": "SessionPersistenceSnapshot",
|
||||
"source": "packages/session-persistence/session-persistence/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventSurface",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionRecord",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionLogSnapshot",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionSurfaceSnapshot",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionTitleObservation",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionTitleObservationResult",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventRecord",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionResultFilter",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventResultFilter",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventSearchDocument",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionSearchCursor",
|
||||
"source": "packages/session-query/session-query/src/cursor.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionSearchRequest",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventSearchRequest",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionSearchPage",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventSearchPage",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventSearchHit",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionSearchHit",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionLineageNode",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionLineageTrace",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionQueryErrorCode",
|
||||
"source": "packages/session-query/session-query/src/config.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventReadRequest",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventWindow",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventTraceRequest",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventTrace",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/session-query.zh.md",
|
||||
"symbol": "SessionEventTraceObservation",
|
||||
"source": "packages/session-query/session-query/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolOutputDefinition",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolDefinition",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ValueSchemaSpec",
|
||||
"source": "packages/core/tools/src/schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ParameterPropertySpec",
|
||||
"source": "packages/core/tools/src/schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ParameterSchemaSpec",
|
||||
"source": "packages/core/tools/src/schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "InferValue",
|
||||
"source": "packages/core/tools/src/schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "InferArgs",
|
||||
"source": "packages/core/tools/src/schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolExecutionToken",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolExecutionInput",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolExecution",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolDispatchExecution",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolExecutionMode",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolRunContext",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolGuard",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolRestriction",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolFailure",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolExecutionSuccess",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolExecutionFailure",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ToolExecutionResult",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "PreToolDecision",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "PostToolDecision",
|
||||
"source": "packages/core/tools/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "JsonSchemaScalar",
|
||||
"source": "packages/core/tools/src/json-schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "JsonSchemaType",
|
||||
"source": "packages/core/tools/src/json-schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "JsonSchemaNode",
|
||||
"source": "packages/core/tools/src/json-schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/tools.zh.md",
|
||||
"symbol": "ObjectJsonSchema",
|
||||
"source": "packages/core/tools/src/json-schema.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/user-interaction.zh.md",
|
||||
"symbol": "AskUserQuestionOption",
|
||||
"source": "packages/ui/user-interaction/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/user-interaction.zh.md",
|
||||
"symbol": "AskUserQuestionItem",
|
||||
"source": "packages/ui/user-interaction/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/user-interaction.zh.md",
|
||||
"symbol": "AskUserQuestionRequest",
|
||||
"source": "packages/ui/user-interaction/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/user-interaction.zh.md",
|
||||
"symbol": "AskUserQuestionAnswerItem",
|
||||
"source": "packages/ui/user-interaction/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/user-interaction.zh.md",
|
||||
"symbol": "AskUserQuestionAnswer",
|
||||
"source": "packages/ui/user-interaction/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/user-interaction.zh.md",
|
||||
"symbol": "UserInteractionProvider",
|
||||
"source": "packages/ui/user-interaction/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/user-interaction.zh.md",
|
||||
"symbol": "UserInteractionError",
|
||||
"source": "packages/ui/user-interaction/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/approval.zh.md",
|
||||
"symbol": "ApprovalRequestId",
|
||||
"source": "packages/ui/user-approval/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/approval.zh.md",
|
||||
"symbol": "ApprovalOutcome",
|
||||
"source": "packages/ui/user-approval/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/approval.zh.md",
|
||||
"symbol": "ApprovalPolicy",
|
||||
"source": "packages/ui/user-approval/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/approval.zh.md",
|
||||
"symbol": "ApprovalRequest",
|
||||
"source": "packages/ui/user-approval/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "DshEnvironmentKey",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "DshEnvironment",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "BashExecRequest",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "BashExecSpec",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "BashRunResult",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "BashSandboxInfo",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "CollectedOutput",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "BashProcess",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/bash.zh.md",
|
||||
"symbol": "BashProcessRead",
|
||||
"source": "packages/bash/bash/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/sandbox.zh.md",
|
||||
"symbol": "SandboxMode",
|
||||
"source": "packages/sandbox/sandbox/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/sandbox.zh.md",
|
||||
"symbol": "ConfinedSandboxMode",
|
||||
"source": "packages/sandbox/sandbox/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/sandbox.zh.md",
|
||||
"symbol": "SandboxExecutionPolicy",
|
||||
"source": "packages/sandbox/sandbox/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/sandbox.zh.md",
|
||||
"symbol": "SandboxEnforcement",
|
||||
"source": "packages/sandbox/sandbox/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/sandbox.zh.md",
|
||||
"symbol": "SandboxPolicy",
|
||||
"source": "packages/sandbox/sandbox/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/sandbox.zh.md",
|
||||
"symbol": "SandboxPolicyRequest",
|
||||
"source": "packages/sandbox/sandbox-policy/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/sandbox.zh.md",
|
||||
"symbol": "ConfinedArgv",
|
||||
"source": "packages/sandbox/sandbox/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/code-runtime.zh.md",
|
||||
"symbol": "CodeJsonValue",
|
||||
"source": "packages/code-runtime/code-runtime/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/code-runtime.zh.md",
|
||||
"symbol": "CodeRunRequest",
|
||||
"source": "packages/code-runtime/code-runtime/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/code-runtime.zh.md",
|
||||
"symbol": "CodeRunResult",
|
||||
"source": "packages/code-runtime/code-runtime/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/code-runtime.zh.md",
|
||||
"symbol": "CodeBindingNamespace",
|
||||
"source": "packages/code-runtime/code-runtime/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/code-runtime.zh.md",
|
||||
"symbol": "CodeBindingErrorClass",
|
||||
"source": "packages/code-runtime/code-runtime/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/code-runtime.zh.md",
|
||||
"symbol": "CodeBindingFunction",
|
||||
"source": "packages/code-runtime/code-runtime/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/code-runtime.zh.md",
|
||||
"symbol": "CodeRunFailure",
|
||||
"source": "packages/code-runtime/code-runtime/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsTarget",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsTargetKey",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsVersion",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsInfo",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsPathInfo",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsDirEntry",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsWriteIntent",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsWriteOutcome",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsEditRequest",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsEditOutcome",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsErrorCode",
|
||||
"source": "packages/fs/fs/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FsPolicyExec",
|
||||
"source": "packages/fs/fs-policy/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/filesystem.zh.md",
|
||||
"symbol": "FileReadOutcome",
|
||||
"source": "packages/fs/tool-fs/src/read-render.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "SkillSource",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "SkillResourceBase",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "SkillSummary",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "SkillCandidate",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "SkillDefinition",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "SkillRegistration",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "SkillLookupOptions",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "SkillProvider",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/skills.zh.md",
|
||||
"symbol": "Config",
|
||||
"source": "packages/skill/skill/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/compaction.zh.md",
|
||||
"symbol": "CompactionResult",
|
||||
"source": "packages/compact/compact/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/compaction.zh.md",
|
||||
"symbol": "CompactionTrigger",
|
||||
"source": "packages/compact/compact/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/compaction.zh.md",
|
||||
"symbol": "PrunedEntry",
|
||||
"source": "packages/compact/compact-tool-result-prune/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/compaction.zh.md",
|
||||
"symbol": "PruneResult",
|
||||
"source": "packages/compact/compact-tool-result-prune/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/subagent.zh.md",
|
||||
"symbol": "SubagentCapabilities",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/subagent.zh.md",
|
||||
"symbol": "SubagentStartRequest",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/subagent.zh.md",
|
||||
"symbol": "SubagentResult",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/subagent.zh.md",
|
||||
"symbol": "SubagentStopReasonMap",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/subagent.zh.md",
|
||||
"symbol": "SubagentRun",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/subagent.zh.md",
|
||||
"symbol": "SubagentProvider",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/web.zh.md",
|
||||
"symbol": "WebSearchRequest",
|
||||
"source": "packages/web/web/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/web.zh.md",
|
||||
"symbol": "WebSearchResult",
|
||||
"source": "packages/web/web/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/web.zh.md",
|
||||
"symbol": "WebSearchSource",
|
||||
"source": "packages/web/web/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/web.zh.md",
|
||||
"symbol": "WebFetchRequest",
|
||||
"source": "packages/web/web/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/web.zh.md",
|
||||
"symbol": "WebFetchResult",
|
||||
"source": "packages/web/web/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/web.zh.md",
|
||||
"symbol": "WebFetchBody",
|
||||
"source": "packages/web/web/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/workflow.zh.md",
|
||||
"symbol": "WorkflowStartRequest",
|
||||
"source": "packages/workflow/workflow/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/workflow.zh.md",
|
||||
"symbol": "WorkflowMeta",
|
||||
"source": "packages/workflow/workflow/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/workflow.zh.md",
|
||||
"symbol": "WorkflowResult",
|
||||
"source": "packages/workflow/workflow/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/core-data-structures/workflow.zh.md",
|
||||
"symbol": "WorkflowRun",
|
||||
"source": "packages/workflow/workflow/src/types.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -59,7 +59,10 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/client/ui-trajectory': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/client/ui-workspace': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/client/ui-theme': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/client/i18n': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/client/ui-settings': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/client/ui-settings-general': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/client/ui-models': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/client/locale': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/client/web': { kind: 'none', reason: 'Browser-side UI plugin layer; registers no model surface.' },
|
||||
'packages/examples/agent-spine-demo': { kind: 'indirect', reason: 'The bundle only mounts model-facing child plugins.' },
|
||||
'packages/fs/fs': { kind: 'indirect', reason: 'The service interface delegates model rendering to dsh-tool-fs.' },
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* Enforce complete English/Chinese pairs, matching structure, and recorded git
|
||||
* blob hashes under the bilingual manifest. Required files and date-named docs
|
||||
* at or after `requiredSince` must be paired; excluded docs may have neither a
|
||||
* counterpart nor sidecar. `--list` reports state and `--write` records both
|
||||
* sides after human review. Translation quality remains a review responsibility.
|
||||
* blob hashes for every in-scope document. The manifest contains only explicit
|
||||
* exclusions, which may have neither a counterpart nor a sidecar.
|
||||
* `--list` reports state and `--write` records both sides after human review.
|
||||
* Translation quality remains a review responsibility.
|
||||
* See `docs/i18n/README.md` for the owning contract.
|
||||
*/
|
||||
|
||||
@@ -11,11 +11,11 @@ import { createHash } from 'node:crypto'
|
||||
import { existsSync, globSync, readFileSync, writeFileSync } from 'node:fs'
|
||||
import { basename, join, resolve, sep } from 'node:path'
|
||||
import {
|
||||
datedDocumentDate,
|
||||
linksTo,
|
||||
parseTranslationMarkdown,
|
||||
parseTranslationPairingManifest,
|
||||
requiresPairByDate,
|
||||
isTranslationScopeFile,
|
||||
TRANSLATION_SCOPE_GLOB_EXCLUDES,
|
||||
translationStructureDiff,
|
||||
translationStructureSignature,
|
||||
} from './translation-pairing.ts'
|
||||
@@ -24,17 +24,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'))
|
||||
@@ -92,7 +87,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()
|
||||
@@ -118,34 +116,17 @@ if (writeMode) {
|
||||
const errors: string[] = []
|
||||
const state = new Map<string, 'ok' | 'out-of-sync' | 'missing'>()
|
||||
|
||||
// 1. Required pairs exist.
|
||||
for (const req of manifest.required) {
|
||||
if (!existsSync(join(root, req))) {
|
||||
errors.push(`${req}: listed in translation-pairing.manifest.json \`required\` but the file does not exist`)
|
||||
continue
|
||||
}
|
||||
const { zh } = pairPaths(req)
|
||||
if (!existsSync(join(root, zh))) {
|
||||
errors.push(`${req}: required to have a translation, but ${zh} does not exist`)
|
||||
state.set(req, 'missing')
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Date-named documents (Agent Notes) dated on/after the requiredSince cutoff merge
|
||||
// bilingual: a new Agent Note lands with its pair or not at all. Deterministic from
|
||||
// the filename alone — no git history, so it holds on shallow CI checkouts.
|
||||
// 1. Every discovered, non-excluded source merges bilingual.
|
||||
for (const source of sources) {
|
||||
if (isExcluded(source)) continue
|
||||
const date = datedDocumentDate(source)
|
||||
if (!requiresPairByDate(source, manifest.requiredSince) || date === undefined) continue
|
||||
const { zh } = pairPaths(source)
|
||||
if (!existsSync(join(root, zh))) {
|
||||
errors.push(`${source}: dated ${date} — documents dated on/after ${manifest.requiredSince} merge bilingual (docs/i18n/README.md); add the counterpart and record the pair`)
|
||||
errors.push(`${source}: in-scope documentation must merge bilingual (docs/i18n/README.md); add the counterpart and record the pair`)
|
||||
state.set(source, 'missing')
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Every pair that exists at all is complete and consistent. Anchor on the
|
||||
// 2. Every pair that exists at all is complete and consistent. Anchor on the
|
||||
// union of .zh.md files and .i18n.yaml records so a half-deleted pair is
|
||||
// caught from either remnant.
|
||||
const pairAnchors = new Set<string>()
|
||||
@@ -205,7 +186,7 @@ for (const source of [...pairAnchors].sort()) {
|
||||
if (!state.has(source)) state.set(source, 'ok')
|
||||
}
|
||||
|
||||
// Complete the state map for --list: any in-scope, non-excluded document with no pair yet is backlog.
|
||||
// Complete the state map for --list: any in-scope, non-excluded document with no pair is missing.
|
||||
for (const source of sources) {
|
||||
if (!isExcluded(source) && !state.has(source)) state.set(source, 'missing')
|
||||
}
|
||||
@@ -214,9 +195,7 @@ if (listMode) {
|
||||
const order = { 'out-of-sync': 0, missing: 1, ok: 2 } as const
|
||||
const rows = [...state.entries()].sort((a, b) => order[a[1]] - order[b[1]] || a[0].localeCompare(b[0]))
|
||||
for (const [file, status] of rows) {
|
||||
const required = manifest.required.includes(file)
|
||||
const tag = required ? ' (required)' : requiresPairByDate(file, manifest.requiredSince) ? ' (required by date)' : ' (backlog)'
|
||||
console.log(`${status.padEnd(11)} ${file}${status === 'missing' ? tag : ''}`)
|
||||
console.log(`${status.padEnd(11)} ${file}${status === 'missing' ? ' (required)' : ''}`)
|
||||
}
|
||||
const counts = { 'ok': 0, 'out-of-sync': 0, 'missing': 0 }
|
||||
for (const status of state.values()) counts[status]++
|
||||
@@ -225,7 +204,7 @@ if (listMode) {
|
||||
}
|
||||
|
||||
if (errors.length === 0) {
|
||||
console.log(`verify-translation-pairing: ${pairAnchors.size} pair(s) checked against ${manifest.required.length} required, all consistent.`)
|
||||
console.log(`verify-translation-pairing: ${pairAnchors.size} pair(s) checked across all in-scope documentation, all consistent.`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@
|
||||
* declaration; `public-api` entries preserve a class's body-stripped public
|
||||
* declaration. Blocks and entries have a one-to-one relationship; comparison
|
||||
* ignores whitespace and non-JSDoc comments but preserves declaration
|
||||
* structure and every original JSDoc comment.
|
||||
* structure and every original JSDoc comment. Byte-identical `.zh.md` blocks
|
||||
* reuse the manifest-backed check of their unsuffixed sibling.
|
||||
*/
|
||||
|
||||
import { globSync, readFileSync, existsSync } from 'node:fs'
|
||||
import { resolve, sep } from 'node:path'
|
||||
import ts from 'typescript'
|
||||
import { partitionPairedMarkdownDerivatives } from './paired-markdown-derivatives.ts'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
|
||||
@@ -223,7 +225,12 @@ const docSet = new Set<string>()
|
||||
for (const pattern of MARKDOWN_GLOBS) {
|
||||
for (const match of globSync(pattern, { cwd: root })) docSet.add(match.split(sep).join('/'))
|
||||
}
|
||||
const blocks: EquivBlock[] = [...docSet].sort().flatMap(extractEquivBlocks)
|
||||
const extractedBlocks: EquivBlock[] = [...docSet].sort().flatMap(extractEquivBlocks)
|
||||
const { primary: blocks, derivatives } = partitionPairedMarkdownDerivatives(
|
||||
extractedBlocks,
|
||||
block => block.doc,
|
||||
block => `${block.projection ?? 'declaration'}\0${block.code}`,
|
||||
)
|
||||
|
||||
const errors: string[] = []
|
||||
// A manifest entry naming a doc that does not exist (or is outside the scanned
|
||||
@@ -299,11 +306,11 @@ for (const e of entries) {
|
||||
}
|
||||
|
||||
if (errors.length === 0) {
|
||||
console.log(`verify-type-equiv: ${verified} type-equiv block(s) match source structure and JSDoc (1:1 with manifest).`)
|
||||
console.log(`verify-type-equiv: ${verified} type-equiv block(s) match source structure and JSDoc (1:1 with manifest); ${derivatives.length} paired derivative(s).`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
console.error('verify-type-equiv: type-equiv verification failed:')
|
||||
for (const e of errors) console.error(` ${e}`)
|
||||
console.error(`\n(checked ${blocks.length} block(s) across ${new Set(blocks.map(b => b.doc)).size} doc(s); manifest at scripts/type-equiv.manifest.json)`)
|
||||
console.error(`\n(checked ${blocks.length} primary block(s) across ${new Set(blocks.map(b => b.doc)).size} doc(s), ${derivatives.length} paired derivative(s); manifest at scripts/type-equiv.manifest.json)`)
|
||||
process.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user