docs(agents): pointerize duplicated DB rules and add index width guard
18 个服务与公共模块重复复制的 314 字符数据库访问约束改为指向后端根唯一定义,并给索引门禁增加防退化校验。 - 18 处重复段落合并为一句指针,规则正文只在后端根 AGENTS.md 维护 - 门禁新增路由表行宽上限(根/Admin/Client/后端根/海外 PDA),防止业务规则重新回流表格 - 同步包含此前未提交的链接与锚点、表格存在性校验 - 仅索引文档与门禁脚本,未改动业务代码
This commit is contained in:
@@ -74,11 +74,63 @@ function Assert-MarkdownTableUnique([string]$path, [string]$heading, [int]$keyCo
|
||||
if ($keyColumn -ge $cells.Count -or $cells[$keyColumn] -match '^:?-{3,}:?$') { continue }
|
||||
$keys.Add($cells[$keyColumn])
|
||||
}
|
||||
if (-not $inside) {
|
||||
Add-Failure "$scope 缺少章节: $heading"
|
||||
return
|
||||
}
|
||||
if ($keys.Count -lt 2) {
|
||||
Add-Failure "$scope 缺少表头或入口行: $heading"
|
||||
}
|
||||
$keys | Group-Object | Where-Object Count -gt 1 | ForEach-Object {
|
||||
Add-Failure "$scope 存在重复索引键: $($_.Name)($($_.Count) 次)"
|
||||
}
|
||||
}
|
||||
|
||||
# 只检查指定文档的本地 Markdown 链接与标题锚点,不递归读取目录或把代码片段当路径。
|
||||
function Assert-MarkdownLinks([string]$path, [string]$text, [string]$scope) {
|
||||
foreach ($link in [regex]::Matches($text, '(?<!!)\[[^\]\r\n]+\]\((?<target><[^>\r\n]+>|[^\s)]+)\)')) {
|
||||
$target = $link.Groups['target'].Value.Trim([char[]]'<>')
|
||||
if ($target -match '^[a-zA-Z][a-zA-Z0-9+.-]*:') { continue }
|
||||
$parts = $target -split '#', 2
|
||||
$linkedPath = if ($parts[0]) { Join-Path (Split-Path -Parent $path) ([Uri]::UnescapeDataString($parts[0])) } else { $path }
|
||||
$script:checked++
|
||||
if (-not (Test-Path -LiteralPath $linkedPath -PathType Leaf)) {
|
||||
Add-Failure "$scope 链接目标不存在: $target"
|
||||
continue
|
||||
}
|
||||
if ($parts.Count -lt 2 -or -not $parts[1] -or [IO.Path]::GetExtension($linkedPath) -ne '.md') { continue }
|
||||
$script:checked++
|
||||
$linkedText = if ($linkedPath -eq $path) { $text } else { Get-Content -LiteralPath $linkedPath -Raw }
|
||||
$headingIds = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal)
|
||||
foreach ($heading in [regex]::Matches($linkedText, '(?m)^#{1,6}[ \t]+(.+?)\r?$')) {
|
||||
$title = $heading.Groups[1].Value -replace '[ \t]+#+[ \t]*$', ''
|
||||
$slug = [regex]::Replace($title.Trim().ToLowerInvariant(), '[^\p{L}\p{M}\p{N}_\s-]', '') -replace '\s+', '-'
|
||||
$headingId = $slug
|
||||
$suffix = 0
|
||||
while ($headingIds.Contains($headingId)) { $suffix++; $headingId = "$slug-$suffix" }
|
||||
[void]$headingIds.Add($headingId)
|
||||
}
|
||||
if (-not $headingIds.Contains([Uri]::UnescapeDataString($parts[1]))) {
|
||||
Add-Failure "$scope 标题锚点不存在: $target"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 路由表只放定位,不放规则;单行过宽意味着业务规则又回流到表格。
|
||||
function Assert-TableRowWidth([string]$path, [string]$heading, [int]$maxLength, [string]$scope) {
|
||||
$script:checked++
|
||||
$inside = $false
|
||||
foreach ($line in Get-Content -LiteralPath $path) {
|
||||
if ($line -eq $heading) { $inside = $true; continue }
|
||||
if ($inside -and $line.StartsWith('## ')) { break }
|
||||
if (-not $inside -or -not $line.StartsWith('|')) { continue }
|
||||
if ($line -match '^\|[\s\-:|]+\|$') { continue }
|
||||
if ($line.Length -gt $maxLength) {
|
||||
Add-Failure "$scope 路由行过宽($($line.Length) > $maxLength):$($line.Substring(0, 40))…"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rootAgentsPath = Join-Path $workspaceRoot 'AGENTS.md'
|
||||
$adminAgentsPath = Join-Path $workspaceRoot 'apps/admin-web/AGENTS.md'
|
||||
$clientAgentsPath = Join-Path $workspaceRoot 'apps/client-web/AGENTS.md'
|
||||
@@ -90,6 +142,7 @@ $rootAgents = Read-RequiredText $rootAgentsPath
|
||||
$businessIndexPath = Join-Path $workspaceRoot 'docs/业务快速索引.md'
|
||||
$businessIndex = Read-RequiredText $businessIndexPath
|
||||
Assert-Contains $rootAgents 'docs/业务快速索引.md' '根按需业务索引入口'
|
||||
Assert-MarkdownLinks $rootAgentsPath $rootAgents '根导航与文档链接'
|
||||
Assert-MarkdownTableUnique $businessIndexPath '## 业务快速路由表' 0 '详细业务路由表'
|
||||
Assert-MarkdownTableUnique $businessIndexPath '## 常用说法与歧义分流' 0 '业务歧义分流表'
|
||||
Assert-Contains $businessIndex '## 业务快速路由表' '详细业务表存在'
|
||||
@@ -138,6 +191,13 @@ foreach ($applicationDetailIndex in @(
|
||||
Assert-Path "$($applicationDetailIndex.Base)/docs/../AGENTS.md"
|
||||
}
|
||||
Assert-MarkdownTableUnique $adminAgentsPath '## 模块快速路由' 0 'Admin 短入口表'
|
||||
Assert-TableRowWidth $rootAgentsPath '## 业务快速路由表' 260 '根业务快速路由表'
|
||||
Assert-TableRowWidth $rootAgentsPath '## 项目快速入口' 260 '根项目快速入口'
|
||||
Assert-TableRowWidth $rootAgentsPath '## 专项约束按需入口' 200 '根专项约束表'
|
||||
Assert-TableRowWidth $adminAgentsPath '## 模块快速路由' 200 'Admin 短入口表'
|
||||
Assert-TableRowWidth $clientAgentsPath '## 模块快速查询' 160 'Client 短入口表'
|
||||
Assert-TableRowWidth $backendAgentsPath '## 服务路由(按关键词定位)' 160 '后端根服务路由表'
|
||||
Assert-TableRowWidth $overseasPdaAgentsPath '## 页面与能力快速索引' 200 '海外 PDA 入口表'
|
||||
Assert-MarkdownTableUnique $adminIndexPath '## 模块快速路由' 0 'Admin 详细索引表'
|
||||
Assert-MarkdownTableUnique $clientAgentsPath '## 模块快速查询' 0 'Client 短入口表'
|
||||
Assert-MarkdownTableUnique $clientIndexPath '## 模块快速查询' 0 'Client 详细索引表'
|
||||
@@ -615,7 +675,12 @@ Assert-Contains $rootAgents 'baiyue-firstleg/' '根 AGENTS.md'
|
||||
Assert-MarkdownTableUnique $rootAgentsPath '## 项目快速入口' 0 '根项目表'
|
||||
Assert-MarkdownTableUnique $rootAgentsPath '## 项目快速入口' 1 '根项目目录'
|
||||
Assert-MarkdownTableUnique $rootAgentsPath '## 业务快速路由表' 0 '根业务路由表'
|
||||
Assert-MarkdownTableUnique $rootAgentsPath '## 原型入口索引' 0 '根原型表'
|
||||
Assert-Contains $rootAgents 'docs/业务快速索引.md#原型入口索引' '根按需原型入口'
|
||||
Assert-MarkdownTableUnique $businessIndexPath '## 原型入口索引' 0 '按需原型入口表'
|
||||
$prototypeRoutes = (($businessIndex -split '(?m)^## 原型入口索引\r?$', 2)[1] -split '(?m)^## ', 2)[0]
|
||||
$prototypeFiles = [regex]::Matches($prototypeRoutes, '`([^`]+\.(?:md|html|json|js|vue))`') |
|
||||
ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique
|
||||
foreach ($prototypeFile in $prototypeFiles) { Assert-Path $prototypeFile }
|
||||
|
||||
# 前端当前全部业务模块必须能从应用级索引命中。
|
||||
$applications = @(
|
||||
|
||||
Reference in New Issue
Block a user