крупное обновление документации
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
param(
|
||||
[string]$Root = (Split-Path -Parent $PSScriptRoot)
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$failures = [System.Collections.Generic.List[string]]::new()
|
||||
$warnings = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
$targetsDir = Join-Path $Root 'targets'
|
||||
$targetIndex = Join-Path $targetsDir 'README.md'
|
||||
$targetFiles = Get-ChildItem -LiteralPath $targetsDir -File |
|
||||
Where-Object { $_.Name -match '^\d\d-.*\.md$' } |
|
||||
Sort-Object Name
|
||||
|
||||
$activeTargets = [System.Collections.Generic.List[object]]::new()
|
||||
$statusPattern = '<!-- OPENWC_TARGET:(OPEN|ACTIVE|BLOCKED|DONE) -->'
|
||||
|
||||
foreach ($file in $targetFiles) {
|
||||
$content = Get-Content -Raw -Encoding UTF8 -LiteralPath $file.FullName
|
||||
$matches = [regex]::Matches($content, $statusPattern)
|
||||
if ($matches.Count -ne 1) {
|
||||
$failures.Add("$($file.Name): expected exactly one target status marker, found $($matches.Count)")
|
||||
continue
|
||||
}
|
||||
if ($matches[0].Groups[1].Value -eq 'ACTIVE') {
|
||||
$activeTargets.Add($file)
|
||||
}
|
||||
}
|
||||
|
||||
if ($activeTargets.Count -ne 1) {
|
||||
$failures.Add("Expected exactly one ACTIVE target, found $($activeTargets.Count)")
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $targetIndex)) {
|
||||
$failures.Add('targets/README.md is missing')
|
||||
} else {
|
||||
$indexContent = Get-Content -Raw -Encoding UTF8 -LiteralPath $targetIndex
|
||||
$currentMatch = [regex]::Match($indexContent, '(?s)## Current target\s+`(M\d\d)`[^\r\n]*\[[^\]]+\]\(([^)]+)\)')
|
||||
if (-not $currentMatch.Success) {
|
||||
$failures.Add('Could not parse Current target from targets/README.md')
|
||||
} elseif ($activeTargets.Count -eq 1) {
|
||||
$expectedId = $currentMatch.Groups[1].Value
|
||||
$expectedFile = $currentMatch.Groups[2].Value
|
||||
$activeFile = $activeTargets[0].Name
|
||||
$activeId = 'M' + $activeFile.Substring(0, 2)
|
||||
if ($expectedId -ne $activeId -or $expectedFile -ne $activeFile) {
|
||||
$failures.Add("Current target points to $expectedId/$expectedFile but ACTIVE is $activeId/$activeFile")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$claimsDir = Join-Path $Root 'coordination\claims'
|
||||
$claimIds = @{}
|
||||
$claimPattern = '<!-- OPENWC_CLAIM:([A-Z0-9-]+):([a-z0-9-]+):(\d{4}-\d{2}-\d{2}) -->'
|
||||
|
||||
if (Test-Path -LiteralPath $claimsDir) {
|
||||
$claimFiles = Get-ChildItem -LiteralPath $claimsDir -Filter '*.md' -File |
|
||||
Where-Object { $_.Name -ne 'README.md' }
|
||||
foreach ($file in $claimFiles) {
|
||||
$content = Get-Content -Raw -Encoding UTF8 -LiteralPath $file.FullName
|
||||
$matches = [regex]::Matches($content, $claimPattern)
|
||||
if ($matches.Count -ne 1) {
|
||||
$failures.Add("coordination/claims/$($file.Name): expected exactly one valid OPENWC_CLAIM marker")
|
||||
continue
|
||||
}
|
||||
$claimId = $matches[0].Groups[1].Value
|
||||
if ($claimIds.ContainsKey($claimId)) {
|
||||
$failures.Add("Duplicate claim ID $claimId in $($claimIds[$claimId]) and $($file.Name)")
|
||||
} else {
|
||||
$claimIds[$claimId] = $file.Name
|
||||
}
|
||||
$expiryText = $matches[0].Groups[3].Value
|
||||
$expiry = [datetime]::ParseExact($expiryText, 'yyyy-MM-dd', [Globalization.CultureInfo]::InvariantCulture)
|
||||
if ($expiry.Date -lt (Get-Date).ToUniversalTime().Date) {
|
||||
$warnings.Add("Claim $claimId expired on $expiryText")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($warning in $warnings) {
|
||||
Write-Warning $warning
|
||||
}
|
||||
|
||||
if ($failures.Count -gt 0) {
|
||||
foreach ($failure in $failures) {
|
||||
Write-Error $failure
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Output "Coordination check passed: targets=$($targetFiles.Count) active=$($activeTargets.Count) fallback_claims=$($claimIds.Count) warnings=$($warnings.Count)"
|
||||
exit 0
|
||||
@@ -0,0 +1,101 @@
|
||||
param(
|
||||
[string]$Root = (Split-Path -Parent $PSScriptRoot)
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$failures = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
$requiredFiles = @(
|
||||
'AGENTS.md',
|
||||
'docs\README.md',
|
||||
'docs\DOCUMENTATION_STANDARD.md',
|
||||
'docs\modules\README.md',
|
||||
'docs\modules\TEMPLATE.md',
|
||||
'targets\DEVELOPMENT_ROADMAP.md'
|
||||
)
|
||||
|
||||
foreach ($relative in $requiredFiles) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $Root $relative))) {
|
||||
$failures.Add("Missing required documentation file: $relative")
|
||||
}
|
||||
}
|
||||
|
||||
$agentGuide = Join-Path $Root 'AGENTS.md'
|
||||
if (Test-Path -LiteralPath $agentGuide) {
|
||||
$agentContent = Get-Content -Raw -Encoding UTF8 -LiteralPath $agentGuide
|
||||
if ($agentContent -notmatch 'DOCUMENTATION_STANDARD\.md') {
|
||||
$failures.Add('AGENTS.md does not reference docs/DOCUMENTATION_STANDARD.md')
|
||||
}
|
||||
}
|
||||
|
||||
$requiredHeadings = @(
|
||||
'## Metadata',
|
||||
'## Purpose',
|
||||
'## Non-goals',
|
||||
'## Context and boundaries',
|
||||
'## Public API',
|
||||
'## Inputs and outputs',
|
||||
'## Data flow',
|
||||
'## Ownership, threading and resources',
|
||||
'## Errors, cancellation and recovery',
|
||||
'## Verification',
|
||||
'## Known gaps and risks',
|
||||
'## Source map'
|
||||
)
|
||||
|
||||
$modulesDir = Join-Path $Root 'docs\modules'
|
||||
$moduleCount = 0
|
||||
if (Test-Path -LiteralPath $modulesDir) {
|
||||
$moduleFiles = Get-ChildItem -LiteralPath $modulesDir -Filter '*.md' -File |
|
||||
Where-Object { $_.Name -notin @('README.md', 'TEMPLATE.md') }
|
||||
foreach ($file in $moduleFiles) {
|
||||
++$moduleCount
|
||||
$content = Get-Content -Raw -Encoding UTF8 -LiteralPath $file.FullName
|
||||
foreach ($heading in $requiredHeadings) {
|
||||
if (-not $content.Contains($heading)) {
|
||||
$failures.Add("docs/modules/$($file.Name): missing heading '$heading'")
|
||||
}
|
||||
}
|
||||
if ($content -notmatch '(?s)```mermaid\s+flowchart') {
|
||||
$failures.Add("docs/modules/$($file.Name): missing Mermaid data-flow diagram")
|
||||
}
|
||||
if ($content -notmatch '\| Status \|') {
|
||||
$failures.Add("docs/modules/$($file.Name): missing Status metadata")
|
||||
}
|
||||
if ($content -notmatch '\| Last verified \|') {
|
||||
$failures.Add("docs/modules/$($file.Name): missing Last verified metadata")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$documentationRoots = @('docs', 'targets', 'coordination')
|
||||
foreach ($relativeRoot in $documentationRoots) {
|
||||
$path = Join-Path $Root $relativeRoot
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
continue
|
||||
}
|
||||
foreach ($file in Get-ChildItem -LiteralPath $path -Recurse -Filter '*.md' -File) {
|
||||
$content = Get-Content -Raw -Encoding UTF8 -LiteralPath $file.FullName
|
||||
foreach ($match in [regex]::Matches($content, '\[[^\]]+\]\(([^)#]+)(?:#[^)]+)?\)')) {
|
||||
$target = $match.Groups[1].Value
|
||||
if ($target -match '^(https?:|mailto:)') {
|
||||
continue
|
||||
}
|
||||
$resolved = Join-Path $file.DirectoryName $target
|
||||
if (-not (Test-Path -LiteralPath $resolved)) {
|
||||
$relativeFile = Resolve-Path -LiteralPath $file.FullName -Relative
|
||||
$failures.Add("$relativeFile`: broken relative link '$target'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($failures.Count -gt 0) {
|
||||
foreach ($failure in $failures) {
|
||||
Write-Error $failure
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Output "Documentation check passed: module_specs=$moduleCount required_files=$($requiredFiles.Count)"
|
||||
exit 0
|
||||
Reference in New Issue
Block a user