Files

201 lines
7.9 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[CmdletBinding()]
param(
[string]$Root = (Join-Path $PSScriptRoot '..'),
[switch]$FailOnWarning,
[string[]]$LongPageAllowlist = @(
'Glossary.md',
'INDEX.md',
'concepts/Harness-Engineering-Complete-Guide.md',
'concepts/Meta-Harness.md',
'practices/OpenAI-Codex-Harness-Engineering.md',
'queries/What-is-Harness-Engineering-in-Simple-Terms.md'
)
)
$ErrorActionPreference = 'Stop'
$Root = [IO.Path]::GetFullPath($Root)
$Wiki = Join-Path $Root 'wiki'
$Index = Join-Path $Wiki 'INDEX.md'
$State = Join-Path $Wiki 'compile-results.tsv'
$errors = [Collections.Generic.List[string]]::new()
$warnings = [Collections.Generic.List[string]]::new()
$infos = [Collections.Generic.List[string]]::new()
$longPageAllowlistSet = @{}
foreach ($relativePath in $LongPageAllowlist) {
$normalizedPath = $relativePath.Replace('\', '/')
$longPageAllowlistSet[$normalizedPath] = $true
}
function Add-Error([string]$Message) { $script:errors.Add("ERROR $Message") }
function Add-Warning([string]$Message) { $script:warnings.Add("WARNING $Message") }
function Add-Info([string]$Message) { $script:infos.Add("INFO $Message") }
if (-not (Test-Path -LiteralPath $Wiki -PathType Container)) {
Add-Error "wiki directory does not exist: $Wiki"
}
if (-not (Test-Path -LiteralPath $Index -PathType Leaf)) {
Add-Error "wiki index does not exist: $Index"
}
$pages = @()
if (Test-Path -LiteralPath $Wiki -PathType Container) {
$pages = @(Get-ChildItem -LiteralPath $Wiki -Recurse -File -Filter '*.md' |
Where-Object { $_.DirectoryName -ne (Join-Path $Wiki '_meta') })
}
$byBaseName = @{}
foreach ($page in $pages) {
if ($byBaseName.ContainsKey($page.BaseName)) {
Add-Error "duplicate Wiki basename: $($page.BaseName)"
} else {
$byBaseName[$page.BaseName] = $page.FullName
}
}
foreach ($page in $pages) {
$firstLine = Get-Content -LiteralPath $page.FullName -TotalCount 1
if ($firstLine -ne '---') {
Add-Error "missing frontmatter: $($page.FullName)"
}
$lineCount = @(Get-Content -LiteralPath $page.FullName).Count
if ($lineCount -gt 200) {
$relativeWikiPath = [IO.Path]::GetRelativePath($Wiki, $page.FullName).Replace('\', '/')
if (-not $longPageAllowlistSet.ContainsKey($relativeWikiPath)) {
Add-Warning "long page ($lineCount lines): $($page.FullName)"
}
}
$text = [IO.File]::ReadAllText($page.FullName)
# frontmatter 必填字段(schema 第 3 节:title/type/last_updated/tags/confidence
# 导航页整体豁免(走导航 frontmatter 约定),raw_sources 另对 query 页豁免)
$fmMatch = [regex]::Match($text, '(?s)^---\r?\n(.*?)\r?\n---')
if ($fmMatch.Success) {
$fmText = $fmMatch.Groups[1].Value
$isNavPage = $page.Name -in @('INDEX.md', 'Glossary.md', 'sources.md', '期货研究资料目录.md', '期货资料来源与证据边界.md')
if (-not $isNavPage) {
foreach ($field in @('title', 'type', 'last_updated', 'tags', 'confidence')) {
if (-not [regex]::IsMatch($fmText, "(?m)^$field\s*:")) {
Add-Warning "missing frontmatter field '$field' in $($page.FullName)"
}
}
}
$isQueryPage = [regex]::IsMatch($fmText, '(?m)^type:\s*query')
if (-not $isQueryPage -and -not $isNavPage) {
if (-not [regex]::IsMatch($fmText, '(?m)^raw_sources\s*:')) {
Add-Warning "missing raw_sources field in $($page.FullName)"
}
}
}
foreach ($match in [regex]::Matches($text, '\[\[([^\]|#]+)(?:#[^\]|]+)?(?:\|[^\]]+)?\]\]')) {
$target = $match.Groups[1].Value.Trim()
if (-not $byBaseName.ContainsKey($target)) {
Add-Error "broken Wiki link in $($page.FullName): $target"
}
}
foreach ($match in [regex]::Matches($text, '!\[[^\]]*\]\(([^)]+)\)')) {
$reference = $match.Groups[1].Value.Trim()
if ($reference -match '^https?://' -or $reference -match '^data:') {
continue
}
$candidate = [IO.Path]::GetFullPath([IO.Path]::Combine($page.DirectoryName, $reference))
if (-not (Test-Path -LiteralPath $candidate -PathType Leaf)) {
Add-Error "missing image in $($page.FullName): $reference"
}
}
foreach ($match in [regex]::Matches($text, '(?m)^\s*- path:\s*["'']?([^"''\r\n]+)')) {
$sourcePath = $match.Groups[1].Value.Trim()
if ($sourcePath -match '^https?://') {
continue
}
$candidate = Join-Path $Root $sourcePath
if (-not (Test-Path -LiteralPath $candidate -PathType Leaf)) {
Add-Warning "missing raw_sources path in $($page.FullName): $sourcePath"
}
}
}
if (Test-Path -LiteralPath $Index -PathType Leaf) {
$indexText = [IO.File]::ReadAllText($Index)
foreach ($page in ($pages | Where-Object { $_.Name -notin @('INDEX.md', 'Glossary.md', 'sources.md') })) {
if ($indexText -notmatch [regex]::Escape($page.BaseName)) {
Add-Error "page is not indexed by INDEX.md: $($page.FullName)"
}
}
}
if (Test-Path -LiteralPath $State -PathType Leaf) {
try {
$rows = @(Import-Csv -LiteralPath $State -Delimiter ([char]9))
$seenPaths = @{}
foreach ($row in $rows) {
if ([string]::IsNullOrWhiteSpace($row.raw_path) -or [string]::IsNullOrWhiteSpace($row.hash)) {
Add-Error "compile-results.tsv contains an incomplete row"
continue
}
if ($seenPaths.ContainsKey($row.raw_path)) {
Add-Error "duplicate compile-results raw_path: $($row.raw_path)"
}
$seenPaths[$row.raw_path] = $true
if ($row.hash -match 'initial|pending|to-be-computed|placeholder') {
Add-Error "placeholder hash in compile-results.tsv: $($row.raw_path)"
}
$candidate = Join-Path $Root $row.raw_path
if (-not (Test-Path -LiteralPath $candidate -PathType Leaf)) {
Add-Warning "compile row points to missing raw file: $($row.raw_path)"
}
}
Add-Info "compile rows: $($rows.Count)"
} catch {
Add-Error "compile-results.tsv cannot be parsed: $($_.Exception.Message)"
}
} else {
Add-Error "compile-results.tsv does not exist: $State"
}
# 月度回顾页积累提醒(schema 第 7 节时序内容约定:满 3 页应归档最早为历史层)
$reviewPages = @($pages | Where-Object { $_.BaseName -match '20\d{2}年\d{1,2}月市场回顾$' })
if ($reviewPages.Count -ge 3) {
$reviewNames = ($reviewPages | Sort-Object Name | ForEach-Object { $_.BaseName }) -join '、'
Add-Warning "月度回顾页已积累 $($reviewPages.Count) 页($reviewNames);按 schema 时序内容约定应归档最早页面为历史层(status: historical)并由季度/年度综述承接"
}
$allTextFiles = @(Get-ChildItem -LiteralPath $Root -Recurse -File -Include '*.md', '*.tsv', '*.log' -ErrorAction SilentlyContinue)
$deletedSourceRefs = @()
foreach ($file in $allTextFiles) {
$text = [IO.File]::ReadAllText($file.FullName)
if ($text -match 'MiniMax-M27-Self-Evolution|raw/MiniMax M2\.7|MiniMax M2\.7 开启模型的自我进化') {
if ($text -notmatch '用户已删除|同步移除|delete \|') {
$deletedSourceRefs += $file.FullName
}
}
}
foreach ($file in ($deletedSourceRefs | Sort-Object -Unique)) {
Add-Warning "possible stale MiniMax source/page reference: $file"
}
$linkCount = 0
foreach ($page in $pages) {
$linkCount += ([regex]::Matches([IO.File]::ReadAllText($page.FullName), '\[\[')).Count
}
Add-Info "Wiki Markdown pages: $($pages.Count)"
Add-Info "Wiki links scanned: $linkCount"
Add-Info "long-page allowlist entries: $($longPageAllowlistSet.Count)"
Add-Info "errors: $($errors.Count)"
Add-Info "warnings: $($warnings.Count)"
$errors
$warnings
$infos
if ($errors.Count -gt 0 -or ($FailOnWarning -and $warnings.Count -gt 0)) {
exit 1
}
exit 0