Merge remote-tracking branch 'origin/master' into codex/trim-ai-prose

# Conflicts:
#	docs/config-catalog.md
#	scripts/gen-persistence-catalog.ts
#	scripts/jsdoc.ts
#	scripts/verify-md-wrap.ts
#	scripts/verify-package-paths.ts
This commit is contained in:
Tianyi Cui
2026-07-14 00:47:30 +08:00
21 changed files with 316 additions and 399 deletions

View File

@@ -5,8 +5,9 @@
* outside the check.
*/
import { existsSync, globSync, readdirSync, readFileSync, realpathSync } from 'node:fs'
import { relative, resolve } from 'node:path'
import { existsSync, readdirSync } from 'node:fs'
import { resolve } from 'node:path'
import { findReferenceViolations, uniqueRepoFiles, type ReferenceViolation as Violation } from './repo-files.ts'
const root = resolve(import.meta.dirname, '..')
@@ -55,64 +56,32 @@ const packageNames = realPackageNames()
*/
const PKG_REF = /\bpackages\/[A-Za-z0-9._/-]+/g
/** A broken package reference: a stale root-relative `packages/…` path. */
interface Violation {
file: string
/** 1-based line where the reference appears. */
line: number
ref: string
function isDriftedPackageReference(ref: string): boolean {
if (existsSync(resolve(root, ref))) return false
// Ignore unbuilt `lib/` paths only under an existing depth-two package root:
// CI runs this gate before build, while stale group-less paths must still fail.
const parts = ref.split('/')
const libAt = parts.indexOf('lib')
if (libAt === 3 && existsSync(resolve(root, parts.slice(0, 3).join('/')))) return false
// A missing reference is drift only when a path segment names a live package.
return ref.split('/').slice(1).some(segment => packageNames.has(segment))
}
/**
* Find every DRIFTED `packages/…` reference in one file: a token that does not
* resolve on disk AND names a real package in one of its segments (so it is a
* moved path, not a typo or a not-yet-existing package). The same real-package
* test also screens out a bare `packages` (no segment) and illustrative
* skeletons whose segment is not a package.
*/
/** Find missing package references whose path names a live package; bare paths, typos, and illustrative skeletons do not count. */
function findViolations(absPath: string): Violation[] {
const file = relative(root, absPath)
const source = readFileSync(absPath, 'utf8')
const out: Violation[] = []
const lines = source.split('\n')
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
if (line === undefined) continue
for (const m of line.matchAll(PKG_REF)) {
// Trim a trailing path separator or sentence punctuation that the greedy
// class may have swallowed (`packages/core/tools.` / `…/tools/`).
const ref = m[0].replace(/[./]+$/, '')
if (existsSync(resolve(root, ref))) continue
// Skip unbuilt `lib/` only below a real depth-two package root. A stale
// group-less path still fails; `lib` is not a blanket escape hatch.
const parts = ref.split('/')
const libAt = parts.indexOf('lib')
if (libAt === 3 && existsSync(resolve(root, parts.slice(0, 3).join('/')))) continue
// Only a stale path to a REAL (moved) package is a violation; a segment
// matching a live package name is the drift signal.
const segments = ref.split('/').slice(1)
if (segments.some(seg => packageNames.has(seg))) {
out.push({ file, line: i + 1, ref })
}
}
}
return out
return findReferenceViolations(
root,
absPath,
PKG_REF,
// Remove trailing separators or sentence punctuation matched greedily.
ref => ref.replace(/[./]+$/, ''),
isDriftedPackageReference,
)
}
const all: Violation[] = []
let checked = 0
const seen = new Set<string>()
for (const pattern of PATTERNS) {
for (const match of globSync(pattern, { cwd: root })) {
if (isExcluded(match)) continue
// Dedup by real path: the root/packages CLAUDE.md are symlinks to AGENTS.md.
const real = realpathSync(resolve(root, match))
if (seen.has(real)) continue
seen.add(real)
checked++
all.push(...findViolations(real))
}
}
const files = uniqueRepoFiles(root, PATTERNS, isExcluded)
const all = files.flatMap(file => findViolations(file.real))
const checked = files.length
if (all.length === 0) {
console.log(`verify-package-paths: ${checked} file(s) checked, all packages/* references resolve.`)