From 625c7524ce33c3769353a7deb5b0d572919a9525 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 2 May 2026 10:36:49 +0800 Subject: [PATCH] docs: add phase 13 reviewer kit export --- .../phase13-reviewer-kit-export-workflow.md | 46 ++ ...¸Ž合同模板库建设闭环第十七批执行记录-2026-05-02.md | 68 +++ scripts/export-phase13-reviewer-kit.ps1 | 460 ++++++++++++++++++ scripts/run-legal-knowledge-library-gate.ps1 | 5 + scripts/run-phase13-review-control-center.ps1 | 14 + 5 files changed, 593 insertions(+) create mode 100644 project-docs/phase13-legal-knowledge-template-library/evidence/phase13-reviewer-kit-export-workflow.md create mode 100644 project-docs/阶段13-法律知识库与合同模板库建设闭环第十七批执行记录-2026-05-02.md create mode 100644 scripts/export-phase13-reviewer-kit.ps1 diff --git a/project-docs/phase13-legal-knowledge-template-library/evidence/phase13-reviewer-kit-export-workflow.md b/project-docs/phase13-legal-knowledge-template-library/evidence/phase13-reviewer-kit-export-workflow.md new file mode 100644 index 00000000..7c40c79c --- /dev/null +++ b/project-docs/phase13-legal-knowledge-template-library/evidence/phase13-reviewer-kit-export-workflow.md @@ -0,0 +1,46 @@ +# 阶段 13 复核派发包导出与证据签收套件 + +本文档用于说明如何把第十五批生成的任务包导出为可交付给真实法务、真实业务和签收接收人的角色包。 + +## 一、目标 + +派发包导出解决三个问题: + +1. 真实负责人拿到的是按角色拆分的材料,而不是总表。 +2. 回收结果有统一模板,便于后续 DryRun 校验和受保护回填。 +3. 派发前证据有 SHA256 哈希留痕,便于审计归档。 + +## 二、自动化脚本 + +```powershell +powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\export-phase13-reviewer-kit.ps1 +``` + +脚本默认读取最新 `phase13-review-task-package-gate` 产物,输出到: + +```text +project-docs/qa-artifacts/phase13-reviewer-kit-export// +``` + +## 三、产物结构 + +1. `reviewer-kits/legal/`:法务复核工作单和逐项 Markdown 任务单。 +2. `reviewer-kits/business/`:业务复核工作单和逐项 Markdown 任务单。 +3. `reviewer-kits/ack/`:签收工作单和签收任务单。 +4. `reviewer-kits/return-templates/`:复核结果和签收回收模板。 +5. `reviewer-kits/audit/`:验收追踪看板和派发清单。 +6. `reviewer-kit-sha256-manifest.csv`:派发包哈希清单。 +7. `reviewer-kit-return-checklist.md`:派发和回收清单。 + +## 四、门禁状态 + +1. 缺少任务包来源或必需材料时为 `FAIL`。 +2. 派发包已生成但真实复核输入未齐套时为 `BLOCKED`。 +3. 任务包具备真实派发条件且派发包完整时为 `PASS`。 + +## 五、安全规则 + +1. 派发包不得预置 `APPROVED_FOR_PRODUCTION`。 +2. 回收模板默认 `review_result=PENDING`。 +3. 签收模板默认 `acknowledged=false`。 +4. 没有真实签收和真实复核回填前,不得推进生产发布。 diff --git a/project-docs/阶段13-法律知识库与合同模板库建设闭环第十七批执行记录-2026-05-02.md b/project-docs/阶段13-法律知识库与合同模板库建设闭环第十七批执行记录-2026-05-02.md new file mode 100644 index 00000000..e2a1254a --- /dev/null +++ b/project-docs/阶段13-法律知识库与合同模板库建设闭环第十七批执行记录-2026-05-02.md @@ -0,0 +1,68 @@ +# AI合同平台 阶段 13 法律知识库与合同模板库建设闭环第十七批执行记录(2026-05-02) + +## 一、阶段结论 + +本批次完成 `复核派发包导出与证据签收套件`,当前状态为 `BLOCKED_ON_REAL_DISPATCH_INPUT`。 + +第十六批已经把真实复核负责人和截止日期接入为可校验配置。本批次继续把第十五批任务包导出为可交付给真实法务、真实业务和签收接收人的角色包,并生成回收模板、回收清单和哈希留痕。 + +## 二、本批次交付 + +新增或更新内容: + +1. `scripts/export-phase13-reviewer-kit.ps1`。 +2. `scripts/run-phase13-review-control-center.ps1`,把复核派发包导出纳入总控。 +3. `scripts/run-legal-knowledge-library-gate.ps1`,纳入新增脚本检查。 +4. `project-docs/phase13-legal-knowledge-template-library/evidence/phase13-reviewer-kit-export-workflow.md`。 + +## 三、派发包范围 + +脚本默认读取最新 `phase13-review-task-package-gate` 产物,生成: + +| 产物 | 说明 | +|---|---| +| `reviewer-kits/legal/` | 法务复核工作单与 Markdown 任务单 | +| `reviewer-kits/business/` | 业务复核工作单与 Markdown 任务单 | +| `reviewer-kits/ack/` | 法务和业务签收工作单 | +| `reviewer-kits/return-templates/` | 法务、业务、签收回收模板 | +| `reviewer-kits/audit/` | 验收看板和派发清单 | +| `reviewer-kit-sha256-manifest.csv` | 派发包 SHA256 哈希清单 | +| `reviewer-kit-return-checklist.md` | 派发与回收清单 | + +## 四、自动化门禁 + +门禁规则: + +1. 缺少任务包来源或必需材料为 `FAIL`。 +2. 派发包完整但真实复核输入尚未齐套为 `BLOCKED`。 +3. 任务包可派发且派发包完整为 `PASS`。 +4. 派发包不得预置 `APPROVED_FOR_PRODUCTION` 或 `APPROVED_FOR_REVIEW_IMPORT`。 + +## 五、当前状态 + +当前复核输入仍为占位模板,因此派发包可导出、可哈希留痕,但不能视为真实已派发。门禁保持 `BLOCKED` 是正确状态。 + +## 六、自动化验证结果 + +```text +PHASE13_REVIEWER_KIT_EXPORT_BLOCKED +PHASE13_LEGAL_KNOWLEDGE_LIBRARY_GATE_PASS_WITH_LEGAL_REVIEW_REQUIRED +PHASE13_REVIEW_CONTROL_CENTER_BLOCKED +``` + +关键指标: + +| 指标 | 结果 | +|---|---:| +| 法务工作单 | 22 | +| 业务工作单 | 18 | +| 签收工作单 | 2 | +| 派发包文件 | 50 | +| 哈希留痕 | 50 | +| 总控准入评分 | 2/12 | + +## 七、下一步 + +下一步可以继续自动推进“回收结果预检与差异合并工作台”,让真实复核结果一旦回收,就能自动完成字段校验、差异摘要、证据路径校验和回填前风险提示。 + +阶段 13 第十七批当前状态:`BLOCKED_ON_REAL_DISPATCH_INPUT`。 diff --git a/scripts/export-phase13-reviewer-kit.ps1 b/scripts/export-phase13-reviewer-kit.ps1 new file mode 100644 index 00000000..a4f97215 --- /dev/null +++ b/scripts/export-phase13-reviewer-kit.ps1 @@ -0,0 +1,460 @@ +param( + [string]$TaskPackagePath = "", + [string]$ArtifactRoot = "", + [switch]$FailOnBlocked +) + +$ErrorActionPreference = "Stop" + +$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path +$timestamp = Get-Date -Format "yyyy-MM-ddTHH-mm-ss-fff" + +function Resolve-LatestArtifactDir { + param([string]$RootRelativePath) + $root = Join-Path $repoRoot $RootRelativePath + $latest = Get-ChildItem -LiteralPath $root -Directory -ErrorAction SilentlyContinue | + Sort-Object Name -Descending | + Select-Object -First 1 + if ($latest) { + return $latest.FullName + } + return "" +} + +if ([string]::IsNullOrWhiteSpace($TaskPackagePath)) { + $TaskPackagePath = Resolve-LatestArtifactDir -RootRelativePath "project-docs\qa-artifacts\phase13-review-task-package-gate" +} +if (-not [string]::IsNullOrWhiteSpace($TaskPackagePath) -and -not [System.IO.Path]::IsPathRooted($TaskPackagePath)) { + $TaskPackagePath = Join-Path $repoRoot $TaskPackagePath +} + +if ([string]::IsNullOrWhiteSpace($ArtifactRoot)) { + $ArtifactRoot = Join-Path $repoRoot "project-docs\qa-artifacts\phase13-reviewer-kit-export" +} +if (-not [System.IO.Path]::IsPathRooted($ArtifactRoot)) { + $ArtifactRoot = Join-Path $repoRoot $ArtifactRoot +} + +$artifactDir = Join-Path $ArtifactRoot $timestamp +$kitRoot = Join-Path $artifactDir "reviewer-kits" +$legalKitDir = Join-Path $kitRoot "legal" +$businessKitDir = Join-Path $kitRoot "business" +$ackKitDir = Join-Path $kitRoot "ack" +$returnDir = Join-Path $kitRoot "return-templates" +$auditDir = Join-Path $kitRoot "audit" +New-Item -ItemType Directory -Force -Path $legalKitDir, $businessKitDir, $ackKitDir, $returnDir, $auditDir | Out-Null + +$summaryJson = Join-Path $artifactDir "phase13-reviewer-kit-export-summary.json" +$summaryMd = Join-Path $artifactDir "phase13-reviewer-kit-export-summary.md" +$manifestCsv = Join-Path $artifactDir "reviewer-kit-manifest.csv" +$hashManifestCsv = Join-Path $artifactDir "reviewer-kit-sha256-manifest.csv" +$returnChecklistMd = Join-Path $artifactDir "reviewer-kit-return-checklist.md" +$gitStatusPath = Join-Path $artifactDir "git-status.txt" + +$checks = New-Object System.Collections.Generic.List[object] +$manifestRows = New-Object System.Collections.Generic.List[object] +$hashRows = New-Object System.Collections.Generic.List[object] + +function Add-Check { + param( + [string]$Key, + [string]$Label, + [ValidateSet("pass", "fail", "warn", "skip", "blocked")] + [string]$Status, + [string]$Detail, + [bool]$Required = $true, + [hashtable]$Meta = @{} + ) + + $checks.Add([ordered]@{ + key = $Key + label = $Label + status = $Status + required = $Required + detail = $Detail + meta = $Meta + }) | Out-Null +} + +function Save-Json { + param([string]$Path, [object]$Value) + $Value | ConvertTo-Json -Depth 50 | Set-Content -Encoding UTF8 -Path $Path +} + +function Read-JsonFile { + param([string]$Path) + if ([string]::IsNullOrWhiteSpace($Path) -or -not (Test-Path -LiteralPath $Path -PathType Leaf)) { + return $null + } + return Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json +} + +function Import-CsvRows { + param([string]$Path) + if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { + return @() + } + return @(Import-Csv -LiteralPath $Path) +} + +function Get-MetricValue { + param([object]$Summary, [string]$Name, [object]$Default = 0) + if ($null -eq $Summary -or $null -eq $Summary.metrics) { + return $Default + } + $property = $Summary.metrics.PSObject.Properties[$Name] + if ($null -eq $property -or $null -eq $property.Value) { + return $Default + } + return $property.Value +} + +function Convert-OverallStatusText { + param([string]$Status) + switch ($Status) { + "PASS" { return "通过" } + "WARN" { return "通过但有警告" } + "FAIL" { return "未通过" } + "BLOCKED" { return "阻塞" } + default { return $Status } + } +} + +function Convert-CheckStatusText { + param([string]$Status) + switch ($Status) { + "pass" { return "通过" } + "fail" { return "失败" } + "warn" { return "警告" } + "skip" { return "跳过" } + "blocked" { return "阻塞" } + default { return $Status } + } +} + +function Add-Material { + param( + [string]$SourcePath, + [string]$TargetPath, + [string]$Kind, + [bool]$Required = $true + ) + + if (Test-Path -LiteralPath $SourcePath -PathType Leaf) { + Copy-Item -LiteralPath $SourcePath -Destination $TargetPath -Force + $manifestRows.Add([pscustomobject][ordered]@{ + kind = $Kind + source = $SourcePath + target = $TargetPath + required = $Required + status = "included" + }) | Out-Null + return $true + } + + $manifestRows.Add([pscustomobject][ordered]@{ + kind = $Kind + source = $SourcePath + target = $TargetPath + required = $Required + status = "missing" + }) | Out-Null + return $false +} + +function Write-ReturnTemplate { + param( + [object[]]$Rows, + [string]$Path, + [string]$Role + ) + + $Rows | ForEach-Object { + [pscustomobject][ordered]@{ + review_id = $_.review_id + asset_type = $_.asset_type + asset_id = $_.asset_id + asset_name = $_.asset_name + reviewer_role = $Role + reviewer = $_.assignee + review_date = "" + review_result = "PENDING" + required_changes = "" + evidence_path = $_.evidence_form_path + allow_review_import = "false" + allow_production_release = "false" + notes = "由真实复核人填写;未填写前不得改为 APPROVED" + } + } | Export-Csv -LiteralPath $Path -NoTypeInformation -Encoding UTF8 +} + +function Write-AckReturnTemplate { + param([object[]]$Rows, [string]$Path) + + $Rows | ForEach-Object { + [pscustomobject][ordered]@{ + work_order_id = $_.work_order_id + recipient_role = $_.recipient_role + recipient = $_.recipient + handoff_owner = $_.handoff_owner + acknowledged = "false" + ack_date = "" + package_path = $_.package_path + missing_materials = "" + notes = "由接收人填写;未签收前不得视为真实复核启动" + } + } | Export-Csv -LiteralPath $Path -NoTypeInformation -Encoding UTF8 +} + +if (-not (Test-Path "$HOME\.Codex\skills\gstack\bin")) { + Add-Check -Key "gstack" -Label "gstack 前置检查" -Status "fail" -Detail "gstack 缺失" +} else { + Add-Check -Key "gstack" -Label "gstack 前置检查" -Status "pass" -Detail "GSTACK_OK" +} + +$branch = (git -C $repoRoot rev-parse --abbrev-ref HEAD).Trim() +$commit = (git -C $repoRoot rev-parse HEAD).Trim() +$remoteUrl = (git -C $repoRoot remote get-url origin).Trim() +$gitStatus = git -C $repoRoot status --short +($gitStatus -join [Environment]::NewLine) | Set-Content -Encoding UTF8 -Path $gitStatusPath + +if ($remoteUrl -match "192\.168\.3\.90") { + Add-Check -Key "origin_remote" -Label "origin 远端仓库" -Status "pass" -Detail "origin 指向 192.168.3.90" -Meta @{ branch = $branch; commit = $commit; remote = $remoteUrl } +} else { + Add-Check -Key "origin_remote" -Label "origin 远端仓库" -Status "fail" -Detail "origin 未指向 192.168.3.90" -Meta @{ branch = $branch; commit = $commit; remote = $remoteUrl } +} + +if ($gitStatus) { + Add-Check -Key "working_tree" -Label "工作区状态" -Status "warn" -Required $false -Detail "工作区存在未提交改动;派发包门禁按证据记录" -Meta @{ artifact = $gitStatusPath } +} else { + Add-Check -Key "working_tree" -Label "工作区状态" -Status "pass" -Detail "工作区干净" -Meta @{ artifact = $gitStatusPath } +} + +$taskSummaryPath = if ([string]::IsNullOrWhiteSpace($TaskPackagePath)) { "" } else { Join-Path $TaskPackagePath "phase13-review-task-package-gate-summary.json" } +$taskSummary = Read-JsonFile -Path $taskSummaryPath +if ($null -ne $taskSummary) { + Add-Check -Key "task_package_source" -Label "任务包来源" -Status "pass" -Detail "读取到任务包摘要,状态=$($taskSummary.status)" -Meta @{ path = $taskSummaryPath } +} else { + Add-Check -Key "task_package_source" -Label "任务包来源" -Status "fail" -Detail "缺少任务包摘要" -Meta @{ path = $taskSummaryPath } +} + +$legalWorkOrdersPath = Join-Path $TaskPackagePath "legal-review-work-orders.csv" +$businessWorkOrdersPath = Join-Path $TaskPackagePath "business-review-work-orders.csv" +$ackWorkOrdersPath = Join-Path $TaskPackagePath "handoff-ack-work-orders.csv" +$acceptanceBoardPath = Join-Path $TaskPackagePath "review-acceptance-tracking-board.csv" +$dispatchManifestPath = Join-Path $TaskPackagePath "review-dispatch-manifest.csv" +$sourceWorkOrdersDir = Join-Path $TaskPackagePath "work-orders" + +$legalRows = Import-CsvRows -Path $legalWorkOrdersPath +$businessRows = Import-CsvRows -Path $businessWorkOrdersPath +$ackRows = Import-CsvRows -Path $ackWorkOrdersPath + +$materialsOk = $true +$materialsOk = (Add-Material -SourcePath $legalWorkOrdersPath -TargetPath (Join-Path $legalKitDir "legal-review-work-orders.csv") -Kind "legal_work_orders") -and $materialsOk +$materialsOk = (Add-Material -SourcePath $businessWorkOrdersPath -TargetPath (Join-Path $businessKitDir "business-review-work-orders.csv") -Kind "business_work_orders") -and $materialsOk +$materialsOk = (Add-Material -SourcePath $ackWorkOrdersPath -TargetPath (Join-Path $ackKitDir "handoff-ack-work-orders.csv") -Kind "ack_work_orders") -and $materialsOk +$materialsOk = (Add-Material -SourcePath $acceptanceBoardPath -TargetPath (Join-Path $auditDir "review-acceptance-tracking-board.csv") -Kind "acceptance_board") -and $materialsOk +$materialsOk = (Add-Material -SourcePath $dispatchManifestPath -TargetPath (Join-Path $auditDir "review-dispatch-manifest.csv") -Kind "dispatch_manifest") -and $materialsOk + +foreach ($subdir in @("legal", "business", "ack")) { + $sourceDir = Join-Path $sourceWorkOrdersDir $subdir + $targetDir = Join-Path $kitRoot $subdir + if (Test-Path -LiteralPath $sourceDir -PathType Container) { + $sourceFiles = @(Get-ChildItem -LiteralPath $sourceDir -File -ErrorAction SilentlyContinue) + foreach ($sourceFile in $sourceFiles) { + Copy-Item -LiteralPath $sourceFile.FullName -Destination $targetDir -Force + } + $manifestRows.Add([pscustomobject][ordered]@{ + kind = "$subdir`_markdown_work_orders" + source = $sourceDir + target = $targetDir + required = $true + status = if ($sourceFiles.Count -gt 0) { "included:$($sourceFiles.Count)" } else { "missing" } + }) | Out-Null + if ($sourceFiles.Count -eq 0) { + $materialsOk = $false + } + } else { + $materialsOk = $false + $manifestRows.Add([pscustomobject][ordered]@{ + kind = "$subdir`_markdown_work_orders" + source = $sourceDir + target = $targetDir + required = $true + status = "missing" + }) | Out-Null + } +} + +$legalReturnTemplate = Join-Path $returnDir "legal-review-result-return.csv" +$businessReturnTemplate = Join-Path $returnDir "business-review-result-return.csv" +$ackReturnTemplate = Join-Path $returnDir "handoff-ack-return.csv" +Write-ReturnTemplate -Rows $legalRows -Path $legalReturnTemplate -Role "法务" +Write-ReturnTemplate -Rows $businessRows -Path $businessReturnTemplate -Role "业务" +Write-AckReturnTemplate -Rows $ackRows -Path $ackReturnTemplate + +$manifestRows.Add([pscustomobject][ordered]@{ kind = "legal_return_template"; source = ""; target = $legalReturnTemplate; required = $true; status = "generated" }) | Out-Null +$manifestRows.Add([pscustomobject][ordered]@{ kind = "business_return_template"; source = ""; target = $businessReturnTemplate; required = $true; status = "generated" }) | Out-Null +$manifestRows.Add([pscustomobject][ordered]@{ kind = "ack_return_template"; source = ""; target = $ackReturnTemplate; required = $true; status = "generated" }) | Out-Null + +$checklist = New-Object System.Collections.Generic.List[string] +$checklist.Add("# 阶段 13 复核派发包回收清单") | Out-Null +$checklist.Add("") | Out-Null +$checklist.Add("## 派发前") | Out-Null +$checklist.Add("") | Out-Null +$checklist.Add("1. 确认真实复核输入接入门禁通过。") | Out-Null +$checklist.Add("2. 确认任务包门禁为 PASS 或 WARN,且可派发工作项不为 0。") | Out-Null +$checklist.Add("3. 将 legal、business、ack 三个目录分别交给对应角色。") | Out-Null +$checklist.Add("4. 保存本目录哈希清单,作为派发前证据。") | Out-Null +$checklist.Add("") | Out-Null +$checklist.Add("## 回收后") | Out-Null +$checklist.Add("") | Out-Null +$checklist.Add("1. 回收 `return-templates` 中的结果文件。") | Out-Null +$checklist.Add("2. 运行 `apply-phase13-review-results.ps1` 做 DryRun 校验。") | Out-Null +$checklist.Add("3. 运行进度、审计归档、总控和生产发布门禁。") | Out-Null +$checklist.Add("4. 未通过门禁前不得把任何资产改为生产可发布。") | Out-Null +$checklist | Set-Content -Encoding UTF8 -Path $returnChecklistMd +$manifestRows.Add([pscustomobject][ordered]@{ kind = "return_checklist"; source = ""; target = $returnChecklistMd; required = $true; status = "generated" }) | Out-Null + +$manifestRows | Export-Csv -LiteralPath $manifestCsv -NoTypeInformation -Encoding UTF8 + +$kitFiles = @(Get-ChildItem -LiteralPath $kitRoot -Recurse -File -ErrorAction SilentlyContinue) +foreach ($file in $kitFiles) { + $hash = Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256 + $relative = $file.FullName.Substring($kitRoot.Length).TrimStart("\") + $hashRows.Add([pscustomobject][ordered]@{ + relative_path = $relative + length = $file.Length + sha256 = $hash.Hash + }) | Out-Null +} +$hashRows | Export-Csv -LiteralPath $hashManifestCsv -NoTypeInformation -Encoding UTF8 + +if ($materialsOk) { + Add-Check -Key "kit_materials" -Label "派发包材料" -Status "pass" -Detail "必需派发材料齐套" +} else { + Add-Check -Key "kit_materials" -Label "派发包材料" -Status "fail" -Detail "必需派发材料缺失" +} + +if ($kitFiles.Count -gt 0 -and $hashRows.Count -eq $kitFiles.Count) { + Add-Check -Key "hash_manifest" -Label "派发包哈希清单" -Status "pass" -Detail "已生成 $($hashRows.Count) 条 SHA256 留痕" +} else { + Add-Check -Key "hash_manifest" -Label "派发包哈希清单" -Status "fail" -Detail "哈希清单数量异常" +} + +$taskReadyOrders = [int](Get-MetricValue -Summary $taskSummary -Name "ready_to_dispatch_orders") +$taskAcceptanceRows = [int](Get-MetricValue -Summary $taskSummary -Name "acceptance_tracking_rows") +if ($taskAcceptanceRows -gt 0 -and $taskReadyOrders -eq $taskAcceptanceRows -and @("PASS", "WARN") -contains [string]$taskSummary.status) { + Add-Check -Key "dispatch_readiness" -Label "真实派发条件" -Status "pass" -Detail "任务包已具备真实派发条件" +} else { + Add-Check -Key "dispatch_readiness" -Label "真实派发条件" -Status "blocked" -Detail "任务包尚未具备真实派发条件:ready=$taskReadyOrders / acceptance=$taskAcceptanceRows / status=$($taskSummary.status)" +} + +$approvalLeaks = @($kitFiles | Select-String -Pattern "APPROVED_FOR_PRODUCTION|APPROVED_FOR_REVIEW_IMPORT" -SimpleMatch -List -ErrorAction SilentlyContinue) +if ($approvalLeaks.Count -eq 0) { + Add-Check -Key "no_approval_in_export" -Label "未在派发包中预置批准结论" -Status "pass" -Detail "派发包未预置批准结论" +} else { + Add-Check -Key "no_approval_in_export" -Label "未在派发包中预置批准结论" -Status "fail" -Detail "发现疑似预置批准结论:$($approvalLeaks.Count)" +} + +$failedRequired = @($checks | Where-Object { $_.required -and $_.status -eq "fail" }).Count +$blockedRequired = @($checks | Where-Object { $_.required -and $_.status -eq "blocked" }).Count +$warnings = @($checks | Where-Object { $_.status -eq "warn" }).Count + +$overallStatus = if ($failedRequired -gt 0) { + "FAIL" +} elseif ($blockedRequired -gt 0) { + "BLOCKED" +} elseif ($warnings -gt 0) { + "WARN" +} else { + "PASS" +} + +$metrics = [ordered]@{ + legal_work_orders = $legalRows.Count + business_work_orders = $businessRows.Count + ack_work_orders = $ackRows.Count + kit_files = $kitFiles.Count + hash_manifest_rows = $hashRows.Count + manifest_rows = $manifestRows.Count + task_ready_to_dispatch_orders = $taskReadyOrders + task_acceptance_rows = $taskAcceptanceRows +} + +$summary = [ordered]@{ + name = "phase13-reviewer-kit-export" + status = $overallStatus + generated_at = (Get-Date).ToUniversalTime().ToString("o") + branch = $branch + commit = $commit + remote = $remoteUrl + task_package_path = $TaskPackagePath + checks = $checks.ToArray() + metrics = $metrics + artifacts = [ordered]@{ + summary_json = $summaryJson + summary_md = $summaryMd + kit_root = $kitRoot + manifest = $manifestCsv + hash_manifest = $hashManifestCsv + return_checklist = $returnChecklistMd + git_status = $gitStatusPath + } + totals = [ordered]@{ + total_checks = $checks.Count + failed_required = $failedRequired + blocked_required = $blockedRequired + warnings = $warnings + } +} +Save-Json -Path $summaryJson -Value $summary + +$md = New-Object System.Collections.Generic.List[string] +$md.Add("# 阶段 13 复核派发包导出门禁") | Out-Null +$md.Add("") | Out-Null +$md.Add("- 门禁状态:$(Convert-OverallStatusText -Status $overallStatus)($overallStatus)") | Out-Null +$md.Add("- 生成时间:$($summary.generated_at)") | Out-Null +$md.Add("- 分支:$branch") | Out-Null +$md.Add("- 提交:$commit") | Out-Null +$md.Add("- 任务包来源:$TaskPackagePath") | Out-Null +$md.Add("- 派发包目录:$kitRoot") | Out-Null +$md.Add("") | Out-Null +$md.Add("## 统计") | Out-Null +$md.Add("") | Out-Null +$md.Add("| 指标 | 数量 |") | Out-Null +$md.Add("|---|---:|") | Out-Null +$md.Add("| 法务工作单 | $($metrics.legal_work_orders) |") | Out-Null +$md.Add("| 业务工作单 | $($metrics.business_work_orders) |") | Out-Null +$md.Add("| 签收工作单 | $($metrics.ack_work_orders) |") | Out-Null +$md.Add("| 派发包文件 | $($metrics.kit_files) |") | Out-Null +$md.Add("| 哈希清单 | $($metrics.hash_manifest_rows) |") | Out-Null +$md.Add("| 可派发工作项 | $($metrics.task_ready_to_dispatch_orders) / $($metrics.task_acceptance_rows) |") | Out-Null +$md.Add("") | Out-Null +$md.Add("## 检查项") | Out-Null +$md.Add("") | Out-Null +$md.Add("| 检查项 | 必需 | 状态 | 说明 |") | Out-Null +$md.Add("|---|---:|---|---|") | Out-Null +foreach ($check in $checks) { + $requiredText = if ($check.required) { "是" } else { "否" } + $md.Add("| $($check.label) | $requiredText | $(Convert-CheckStatusText -Status $check.status) | $($check.detail) |") | Out-Null +} +$md.Add("") | Out-Null +$md.Add("## 当前结论") | Out-Null +$md.Add("") | Out-Null +if ($overallStatus -eq "BLOCKED") { + $md.Add("派发包已导出并留痕,但由于真实复核输入和任务包派发条件未齐套,当前不能作为真实已派发证据。") | Out-Null +} elseif ($overallStatus -eq "PASS") { + $md.Add("派发包已具备真实派发条件,可按角色交付并进入签收回收。") | Out-Null +} else { + $md.Add("派发包门禁未完全通过,请查看检查项和清单。") | Out-Null +} +$md | Set-Content -Encoding UTF8 -Path $summaryMd + +Write-Host "PHASE13_REVIEWER_KIT_EXPORT_$overallStatus $artifactDir" +if ($overallStatus -eq "FAIL") { + throw "阶段 13 复核派发包导出门禁未通过。查看:$summaryMd" +} +if ($overallStatus -eq "BLOCKED" -and $FailOnBlocked) { + throw "阶段 13 复核派发包导出门禁被阻塞。查看:$summaryMd" +} diff --git a/scripts/run-legal-knowledge-library-gate.ps1 b/scripts/run-legal-knowledge-library-gate.ps1 index 7d50cb32..6ff1a122 100644 --- a/scripts/run-legal-knowledge-library-gate.ps1 +++ b/scripts/run-legal-knowledge-library-gate.ps1 @@ -317,6 +317,10 @@ function Test-SecretLeak { if (Test-Path -LiteralPath $reviewIntakeScriptPath -PathType Leaf) { $files += Get-Item -LiteralPath $reviewIntakeScriptPath } + $reviewerKitExportScriptPath = Join-Path $repoRoot "scripts\export-phase13-reviewer-kit.ps1" + if (Test-Path -LiteralPath $reviewerKitExportScriptPath -PathType Leaf) { + $files += Get-Item -LiteralPath $reviewerKitExportScriptPath + } foreach ($file in $files) { $match = Select-String -LiteralPath $file.FullName -Pattern $adminPasswordLiteral -List -ErrorAction SilentlyContinue if ($match) { @@ -397,6 +401,7 @@ $commonDocFiles = @( "scripts\run-phase13-review-control-center.ps1", "scripts\run-phase13-review-task-package-gate.ps1", "scripts\run-phase13-review-intake-gate.ps1", + "scripts\export-phase13-reviewer-kit.ps1", "project-docs\phase13-legal-knowledge-template-library\review-intake\reviewer-intake-template.csv", "project-docs\phase13-legal-knowledge-template-library\review-intake\README.md" ) diff --git a/scripts/run-phase13-review-control-center.ps1 b/scripts/run-phase13-review-control-center.ps1 index 5bba7966..af61ff4d 100644 --- a/scripts/run-phase13-review-control-center.ps1 +++ b/scripts/run-phase13-review-control-center.ps1 @@ -212,6 +212,7 @@ $stages = @( [pscustomobject]@{ key = "first_wave"; label = "首批复核包就绪"; root = "project-docs\qa-artifacts\phase13-first-wave-review-readiness"; summary = "phase13-first-wave-review-readiness-summary.json" }, [pscustomobject]@{ key = "review_intake"; label = "真实复核输入接入"; root = "project-docs\qa-artifacts\phase13-review-intake-gate"; summary = "phase13-review-intake-gate-summary.json" }, [pscustomobject]@{ key = "task_package"; label = "复核任务包与验收追踪"; root = "project-docs\qa-artifacts\phase13-review-task-package-gate"; summary = "phase13-review-task-package-gate-summary.json" }, + [pscustomobject]@{ key = "reviewer_kit"; label = "复核派发包导出"; root = "project-docs\qa-artifacts\phase13-reviewer-kit-export"; summary = "phase13-reviewer-kit-export-summary.json" }, [pscustomobject]@{ key = "handoff"; label = "复核交付回收"; root = "project-docs\qa-artifacts\phase13-review-handoff-gate"; summary = "phase13-review-handoff-gate-summary.json" }, [pscustomobject]@{ key = "progress"; label = "复核进度催办"; root = "project-docs\qa-artifacts\phase13-review-progress-gate"; summary = "phase13-review-progress-gate-summary.json" }, [pscustomobject]@{ key = "result_apply"; label = "复核结果回填"; root = "project-docs\qa-artifacts\phase13-review-result-apply"; summary = "phase13-review-result-apply-summary.json" }, @@ -240,6 +241,7 @@ foreach ($stage in $stages) { $firstWave = $summaries["first_wave"] $reviewIntake = $summaries["review_intake"] $taskPackage = $summaries["task_package"] +$reviewerKit = $summaries["reviewer_kit"] $handoff = $summaries["handoff"] $progress = $summaries["progress"] $resultApply = $summaries["result_apply"] @@ -258,6 +260,9 @@ $reviewIntakeStatus = if ($null -eq $reviewIntake) { "MISSING" } else { [string] $taskPackageAcceptanceRows = [int](Get-MetricValue -Summary $taskPackage -Name "acceptance_tracking_rows") $taskPackageReadyOrders = [int](Get-MetricValue -Summary $taskPackage -Name "ready_to_dispatch_orders") $taskPackageBlockedOrders = [int](Get-MetricValue -Summary $taskPackage -Name "blocked_orders") +$reviewerKitFiles = [int](Get-MetricValue -Summary $reviewerKit -Name "kit_files") +$reviewerKitHashRows = [int](Get-MetricValue -Summary $reviewerKit -Name "hash_manifest_rows") +$reviewerKitReadyOrders = [int](Get-MetricValue -Summary $reviewerKit -Name "task_ready_to_dispatch_orders") $handoffBlockers = [int](Get-MetricValue -Summary $handoff -Name "blockers") $handoffAckRows = [int](Get-MetricValue -Summary $handoff -Name "acknowledged_rows") $ackPending = [int](Get-MetricValue -Summary $progress -Name "ack_pending") @@ -275,6 +280,7 @@ Add-Score -Key "legal_reviewer_assigned" -Label "真实法务复核人已分配" Add-Score -Key "business_reviewer_assigned" -Label "真实业务复核人已分配" -Ready ($missingBusinessReviewer -eq 0) -Evidence "missing_business_reviewer_tasks=$missingBusinessReviewer" -Blocker "缺少真实业务复核人" Add-Score -Key "review_intake_ready" -Label "真实复核输入配置已齐套" -Ready ($reviewIntakeStatus -eq "PASS" -and $intakeRequiredRoles -gt 0 -and $intakeReadyRoles -eq $intakeRequiredRoles) -Evidence "intake_status=$reviewIntakeStatus; ready_roles=$intakeReadyRoles; required_roles=$intakeRequiredRoles; blocked_roles=$intakeBlockedRoles" -Blocker "真实复核输入配置未齐套" Add-Score -Key "review_task_package_generated" -Label "复核任务包与验收追踪已生成" -Ready ($taskPackageAcceptanceRows -ge 42) -Evidence "acceptance_tracking_rows=$taskPackageAcceptanceRows; ready_to_dispatch_orders=$taskPackageReadyOrders; blocked_orders=$taskPackageBlockedOrders" -Blocker "复核任务包或验收追踪未齐套" +Add-Score -Key "reviewer_kit_exported" -Label "复核派发包已导出并哈希留痕" -Ready ($reviewerKitFiles -gt 0 -and $reviewerKitHashRows -gt 0) -Evidence "kit_files=$reviewerKitFiles; hash_manifest_rows=$reviewerKitHashRows; ready_orders=$reviewerKitReadyOrders" -Blocker "复核派发包尚未导出" Add-Score -Key "review_due_date_set" -Label "复核截止日期已设置" -Ready (-not $firstWaveDueMissing) -Evidence "due_date_missing=$firstWaveDueMissing" -Blocker "缺少复核截止日期" Add-Score -Key "handoff_acknowledged" -Label "复核交付已签收" -Ready ($ackPending -eq 0 -and $handoffAckRows -gt 0) -Evidence "ack_pending=$ackPending; acknowledged_rows=$handoffAckRows" -Blocker "签收未完成" Add-Score -Key "review_activity_started" -Label "真实复核活动已发生" -Ready ($completedReviews -gt 0 -or $pendingReviews -lt 22) -Evidence "pending_reviews=$pendingReviews; completed_reviews=$completedReviews" -Blocker "没有真实复核活动记录" @@ -301,6 +307,9 @@ if ($reviewIntakeStatus -ne "PASS") { if ($taskPackageAcceptanceRows -lt 42) { Add-NextAction -Priority "P0" -Owner "法律资料运营" -Action "生成复核任务包与验收追踪看板" -Acceptance "acceptance_tracking_rows>=42" -Gate "run-phase13-review-task-package-gate.ps1" } +if ($reviewerKitFiles -eq 0 -or $reviewerKitHashRows -eq 0) { + Add-NextAction -Priority "P0" -Owner "法律资料运营" -Action "导出复核派发包并生成哈希留痕" -Acceptance "kit_files>0 且 hash_manifest_rows>0" -Gate "export-phase13-reviewer-kit.ps1" +} if ($firstWaveDueMissing) { Add-NextAction -Priority "P0" -Owner "交付负责人" -Action "设置首批 P0 复核截止日期" -Acceptance "due_date_missing=false" -Gate "run-phase13-first-wave-review-readiness.ps1 / run-phase13-review-progress-gate.ps1" } @@ -370,6 +379,9 @@ $metrics = [ordered]@{ task_package_acceptance_rows = $taskPackageAcceptanceRows task_package_ready_orders = $taskPackageReadyOrders task_package_blocked_orders = $taskPackageBlockedOrders + reviewer_kit_files = $reviewerKitFiles + reviewer_kit_hash_rows = $reviewerKitHashRows + reviewer_kit_ready_orders = $reviewerKitReadyOrders missing_legal_reviewer_tasks = $missingLegalReviewer missing_business_reviewer_tasks = $missingBusinessReviewer ack_pending = $ackPending @@ -433,6 +445,8 @@ $md.Add("| 复核输入阻塞角色 | $($metrics.review_intake_blocked_roles) |" $md.Add("| 任务包原子验收项 | $($metrics.task_package_acceptance_rows) |") | Out-Null $md.Add("| 任务包可派发项 | $($metrics.task_package_ready_orders) |") | Out-Null $md.Add("| 任务包阻塞项 | $($metrics.task_package_blocked_orders) |") | Out-Null +$md.Add("| 派发包文件 | $($metrics.reviewer_kit_files) |") | Out-Null +$md.Add("| 派发包哈希留痕 | $($metrics.reviewer_kit_hash_rows) |") | Out-Null $md.Add("| 未分配法务复核 | $($metrics.missing_legal_reviewer_tasks) |") | Out-Null $md.Add("| 未分配业务复核 | $($metrics.missing_business_reviewer_tasks) |") | Out-Null $md.Add("| 未完成签收 | $($metrics.ack_pending) |") | Out-Null