mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
docs: generate each subsystem's cordis surface into its own page; delete the flat catalogs
Rebuild of the region machinery (PR3) on the post-#904 Typert projection: renderPageRegion/renderInheritedPage live in dsh-typert-generator beside the projection; scripts/gen-cordis-catalog.ts owns the curated SERVICE_PAGE / EVENT_SCOPE_PAGE / SERVICE_WALK_EXEMPTIONS / LINK_MAP partition (fail-loud in both directions, with the independent Context-merge scan backstopping the projection's blind spot), spliceRegion, and the guarded pair auto-record. docs/cordis-catalog/ is deleted: the flat events/services catalogs dissolve into per-page regions and docs/cordis-catalog/core moves to docs/cordis-api/ with the inherited tier as its own generated page. The partition absorbs the post-regrouping surface: ctx.typert → invariants.md, ctx.directoryPicker → workspace.md, skills/* events → skills.md, and the four launcher-provided tui accessor values join the named exemptions.
This commit is contained in:
@@ -2,13 +2,122 @@
|
||||
* Pure parsing and structural helpers for the bilingual-document pairing
|
||||
* gate. Kept separate from the CLI so corpus discovery and signature behavior
|
||||
* can be regression-tested without reading or mutating the repository tree.
|
||||
* Also the one home of the generated-region grammar and the pair-record
|
||||
* primitives, shared by the pairing gate and the region-injecting generators.
|
||||
*/
|
||||
|
||||
import { createHash } from 'node:crypto'
|
||||
import { basename } from 'node:path'
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown'
|
||||
import { gfmFromMarkdown } from 'mdast-util-gfm'
|
||||
import { gfm } from 'micromark-extension-gfm'
|
||||
import type { Nodes } from 'mdast'
|
||||
|
||||
/** Complete opening marker line: `<!-- BEGIN GENERATED <slug> … -->` (slug captured). */
|
||||
const GENERATED_REGION_BEGIN_LINE = /^<!-- BEGIN GENERATED (\S+)(?: [^>]*)? -->$/
|
||||
/** Complete closing marker line: `<!-- END GENERATED <slug> -->` (slug captured). */
|
||||
const GENERATED_REGION_END_LINE = /^<!-- END GENERATED (\S+) -->$/
|
||||
/** Loose marker detector: any line that LOOKS like a region marker must parse as one. */
|
||||
const GENERATED_REGION_MARKER_HINT = /^<!-- (?:BEGIN|END) GENERATED /
|
||||
|
||||
/**
|
||||
* Extract every generated region (markers included) and the document with
|
||||
* those regions removed. Regions are line-delimited: a marker occupies its
|
||||
* whole line, must be a complete well-formed marker, and the closing slug
|
||||
* must match the opener. The stripped form is what "human content" means for
|
||||
* the region-aware pair-record guard.
|
||||
*
|
||||
* @param content - Full Markdown document text.
|
||||
* @returns The regions in document order and the region-free remainder.
|
||||
* @throws Error on an unopened END, unclosed BEGIN, nested BEGIN, malformed
|
||||
* marker line, or a closing slug that does not match its opener.
|
||||
*/
|
||||
export function partitionGeneratedRegions(content: string): { regions: string[]; stripped: string } {
|
||||
const lines = content.split('\n')
|
||||
const regions: string[] = []
|
||||
const kept: string[] = []
|
||||
let open: { slug: string; lines: string[] } | null = null
|
||||
for (const line of lines) {
|
||||
const begin = GENERATED_REGION_BEGIN_LINE.exec(line)
|
||||
if (begin?.[1]) {
|
||||
if (open) throw new Error('generated region BEGIN marker nested inside an open region')
|
||||
open = { slug: begin[1], lines: [line] }
|
||||
continue
|
||||
}
|
||||
const end = GENERATED_REGION_END_LINE.exec(line)
|
||||
if (end?.[1]) {
|
||||
if (!open) throw new Error('generated region END marker without a BEGIN')
|
||||
if (end[1] !== open.slug) throw new Error(`generated region END slug '${end[1]}' does not match its BEGIN slug '${open.slug}'`)
|
||||
open.lines.push(line)
|
||||
regions.push(open.lines.join('\n'))
|
||||
open = null
|
||||
continue
|
||||
}
|
||||
if (GENERATED_REGION_MARKER_HINT.test(line)) {
|
||||
throw new Error(`malformed generated region marker line: ${JSON.stringify(line)}`)
|
||||
}
|
||||
if (open) open.lines.push(line)
|
||||
else kept.push(line)
|
||||
}
|
||||
if (open) throw new Error('generated region BEGIN marker without an END')
|
||||
return { regions, stripped: kept.join('\n') }
|
||||
}
|
||||
|
||||
/**
|
||||
* Full git blob hash of file content (what `git hash-object` prints).
|
||||
* @param content - Exact file bytes.
|
||||
* @returns The 40-hex-digit SHA-1 blob hash.
|
||||
*/
|
||||
export function blobHash(content: Buffer): string {
|
||||
const hash = createHash('sha1')
|
||||
hash.update(`blob ${content.byteLength}\0`)
|
||||
hash.update(content)
|
||||
return hash.digest('hex')
|
||||
}
|
||||
|
||||
const PAIR_META_LINE = /^([^:#]+\.md): ([0-9a-f]{40})$/
|
||||
|
||||
/**
|
||||
* Parse a `foo.i18n.yaml` consistency record into basename → recorded blob
|
||||
* hash, or undefined when any non-comment line deviates from the exact
|
||||
* `<basename>.md: <40-hex>` shape or repeats a key. Consumers must
|
||||
* additionally require exactly the two expected basenames — a renamed key is
|
||||
* a malformed record, never a silently-missing entry.
|
||||
* @param content - Sidecar file text.
|
||||
* @returns The recorded map, or undefined for a malformed record.
|
||||
*/
|
||||
export function parsePairMeta(content: string): Map<string, string> | undefined {
|
||||
const out = new Map<string, string>()
|
||||
for (const line of content.split('\n')) {
|
||||
if (line === '' || line.startsWith('#')) continue
|
||||
const match = PAIR_META_LINE.exec(line)
|
||||
if (!match?.[1] || !match[2]) return undefined
|
||||
if (out.has(match[1])) return undefined
|
||||
out.set(match[1], match[2])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a `foo.i18n.yaml` consistency record.
|
||||
* @param source - Repo-relative English path.
|
||||
* @param sourceHash - Blob hash of the English side.
|
||||
* @param zh - Repo-relative Chinese path.
|
||||
* @param zhHash - Blob hash of the Chinese side.
|
||||
* @returns The exact sidecar file content.
|
||||
*/
|
||||
export function renderPairMeta(source: string, sourceHash: string, zh: string, zhHash: string): string {
|
||||
return [
|
||||
'# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each',
|
||||
'# side as of the last confirmed-consistent state. Both languages carry equal authority;',
|
||||
'# after editing either side, bring the other along and re-record with:',
|
||||
`# pnpm run verify-translation-pairing --write ${source}`,
|
||||
`${basename(source)}: ${sourceHash}`,
|
||||
`${basename(zh)}: ${zhHash}`,
|
||||
'',
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
/** Validated shape of `scripts/translation-pairing.manifest.json`. */
|
||||
export interface TranslationPairingManifest {
|
||||
/** Source documents exempt from pairing because they are generated, instructional, or bilingual by construction. */
|
||||
|
||||
Reference in New Issue
Block a user