@@ -0,0 +1,687 @@
param (
[ string ] $ArtifactRoot = " " ,
[ string ] $ReviewPacketPath = " " ,
[ string ] $ControlArtifactPath = " " ,
[ string ] $LegalReviewer = " " ,
[ string ] $BusinessReviewer = " " ,
[ string ] $HandoffOwner = " " ,
[ string ] $DueDate = " " ,
[ switch ] $FailOnBlocked
)
$ErrorActionPreference = " Stop "
$repoRoot = ( Resolve-Path ( Join-Path $PSScriptRoot " .. " ) ) . Path
$timestamp = Get-Date -Format " yyyy-MM-ddTHH-mm-ss-fff "
if ( [ string ] :: IsNullOrWhiteSpace ( $ArtifactRoot ) ) {
$ArtifactRoot = Join-Path $repoRoot " project-docs\qa-artifacts\phase13-review-task-package-gate "
}
if ( -not [ System.IO.Path ] :: IsPathRooted ( $ArtifactRoot ) ) {
$ArtifactRoot = Join-Path $repoRoot $ArtifactRoot
}
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 ( $ReviewPacketPath ) ) {
$ReviewPacketPath = Resolve-LatestArtifactDir -RootRelativePath " project-docs\qa-artifacts\phase13-first-wave-review-readiness "
}
if ( -not [ string ] :: IsNullOrWhiteSpace ( $ReviewPacketPath ) -and -not [ System.IO.Path ] :: IsPathRooted ( $ReviewPacketPath ) ) {
$ReviewPacketPath = Join-Path $repoRoot $ReviewPacketPath
}
if ( [ string ] :: IsNullOrWhiteSpace ( $ControlArtifactPath ) ) {
$ControlArtifactPath = Resolve-LatestArtifactDir -RootRelativePath " project-docs\qa-artifacts\phase13-review-control-center "
}
if ( -not [ string ] :: IsNullOrWhiteSpace ( $ControlArtifactPath ) -and -not [ System.IO.Path ] :: IsPathRooted ( $ControlArtifactPath ) ) {
$ControlArtifactPath = Join-Path $repoRoot $ControlArtifactPath
}
$artifactDir = Join-Path $ArtifactRoot $timestamp
$workOrderRoot = Join-Path $artifactDir " work-orders "
$legalWorkOrderDir = Join-Path $workOrderRoot " legal "
$businessWorkOrderDir = Join-Path $workOrderRoot " business "
$ackWorkOrderDir = Join-Path $workOrderRoot " ack "
New-Item -ItemType Directory -Force -Path $legalWorkOrderDir , $businessWorkOrderDir , $ackWorkOrderDir | Out-Null
$summaryJson = Join-Path $artifactDir " phase13-review-task-package-gate-summary.json "
$summaryMd = Join-Path $artifactDir " phase13-review-task-package-gate-summary.md "
$legalPackageCsv = Join-Path $artifactDir " legal-review-work-orders.csv "
$businessPackageCsv = Join-Path $artifactDir " business-review-work-orders.csv "
$ackPackageCsv = Join-Path $artifactDir " handoff-ack-work-orders.csv "
$acceptanceBoardCsv = Join-Path $artifactDir " review-acceptance-tracking-board.csv "
$dispatchManifestCsv = Join-Path $artifactDir " review-dispatch-manifest.csv "
$operatorGuideMd = Join-Path $artifactDir " review-task-package-operator-guide.md "
$gitStatusPath = Join-Path $artifactDir " git-status.txt "
$checks = New-Object System . Collections . Generic . List [ object ]
$legalRows = New-Object System . Collections . Generic . List [ object ]
$businessRows = New-Object System . Collections . Generic . List [ object ]
$ackRows = New-Object System . Collections . Generic . List [ object ]
$acceptanceRows = New-Object System . Collections . Generic . List [ object ]
$dispatchRows = 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 60 | 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 Test-PendingValue {
param ( [ string ] $Value )
if ( [ string ] :: IsNullOrWhiteSpace ( $Value ) ) {
return $true
}
return @ (
" pending " ,
" tbd " ,
" 待指定 " ,
" 待指定真实法务复核人 " ,
" 待指定真实业务复核人 " ,
" 待指定交付负责人 " ,
" 待法务复核 " ,
" 待业务复核 " ,
" 待法务和业务复核 " ,
" 待复核 " ,
" 待确认 "
) -contains $Value . Trim ( ) . ToLowerInvariant ( )
}
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 Get-WorkOrderStatus {
param ( [ string ] $Assignee , [ string ] $DueDateValue )
if ( Test-PendingValue -Value $Assignee ) {
return " PENDING_ASSIGNMENT "
}
if ( [ string ] :: IsNullOrWhiteSpace ( $DueDateValue ) ) {
return " PENDING_DUE_DATE "
}
return " READY_TO_DISPATCH "
}
function New-ReviewWorkOrder {
param (
[ object ] $Task ,
[ ValidateSet ( " legal_review " , " business_review " ) ]
[ string ] $OrderType ,
[ string ] $Assignee ,
[ string ] $AssigneeRole ,
[ string ] $WorkOrderPath
)
$status = Get-WorkOrderStatus -Assignee $Assignee -DueDateValue $DueDate
$criteria = if ( $OrderType -eq " legal_review " ) {
" 法源、条款引用、适用边界、风险等级、修改建议和人工复核提示均经真实法务确认 "
} else {
" 变量、默认值、枚举、业务适用场景、责任上限和试点可用性均经真实业务负责人确认 "
}
$requiredOutput = if ( $OrderType -eq " legal_review " ) {
" 在复核结果回填模板中填写法务复核人、复核日期、复核结果、修改要求和依据说明 "
} else {
" 在复核结果回填模板中填写业务复核人、复核日期、复核结果、修改要求和业务适用说明 "
}
return [ pscustomobject][ordered ] @ {
work_order_id = " $( $OrderType . ToUpperInvariant ( ) ) - $( $Task . review_id ) "
order_type = $OrderType
priority = $Task . priority
review_lane = $Task . review_lane
review_id = $Task . review_id
asset_type = $Task . asset_type
asset_type_text = $Task . asset_type_text
asset_id = $Task . asset_id
asset_name = $Task . asset_name
source_ids = $Task . source_ids
primary_path = $Task . primary_path
secondary_path = $Task . secondary_path
assignee_role = $AssigneeRole
assignee = $Assignee
due_date = $DueDate
current_review_result = $Task . review_result
current_review_status = $Task . review_status
required_action = $Task . required_changes
required_output = $requiredOutput
acceptance_criteria = $criteria
evidence_form_path = $Task . evidence_form_path
work_order_path = $WorkOrderPath
next_gate = " apply-phase13-review-results.ps1 -> run-phase13-review-progress-gate.ps1 -> run-phase13-review-control-center.ps1 "
status = $status
notes = " 不得由自动化脚本代填 APPROVED;必须由真实复核人签署或留痕后回填 "
}
}
function Add-AcceptanceRow {
param ( [ object ] $Order , [ string ] $BlockerArea )
$acceptanceRows . Add ( [ pscustomobject][ordered ] @ {
acceptance_id = " ACCEPT- $( $Order . work_order_id ) "
acceptance_type = $Order . order_type
blocker_area = $BlockerArea
review_id = $Order . review_id
asset_id = $Order . asset_id
owner_role = $Order . assignee_role
assignee = $Order . assignee
due_date = $Order . due_date
required_output = $Order . required_output
acceptance_criteria = $Order . acceptance_criteria
evidence_target = $Order . evidence_form_path
current_status = $Order . status
next_gate = $Order . next_gate
} ) | Out-Null
}
function Write-ReviewWorkOrderMd {
param ( [ object ] $Order , [ string ] $Path )
$lines = New-Object System . Collections . Generic . List [ string ]
$roleText = if ( $Order . order_type -eq " legal_review " ) { " 法务复核 " } else { " 业务复核 " }
$lines . Add ( " # 阶段 13 $roleText 任务单: $( $Order . review_id ) " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " ## 任务对象 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " | 字段 | 值 | " ) | Out-Null
$lines . Add ( " |---|---| " ) | Out-Null
$lines . Add ( " | 工作项 | `$ ( $Order .work_order_id) ` | " ) | Out-Null
$lines . Add ( " | 资产 | `$ ( $Order .asset_id) ` : $( $Order . asset_name ) | " ) | Out-Null
$lines . Add ( " | 类型 | $( $Order . asset_type_text ) | " ) | Out-Null
$lines . Add ( " | 复核人 | $( $Order . assignee ) | " ) | Out-Null
$lines . Add ( " | 截止日期 | $( $Order . due_date ) | " ) | Out-Null
$lines . Add ( " | 主文件 | `$ ( $Order .primary_path) ` | " ) | Out-Null
$lines . Add ( " | 辅助文件 | `$ ( $Order .secondary_path) ` | " ) | Out-Null
$lines . Add ( " | 法源或依赖 | `$ ( $Order .source_ids) ` | " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " ## 执行步骤 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " 1. 打开主文件和辅助文件,逐项核对资料内容。 " ) | Out-Null
$lines . Add ( " 2. 按本任务单的验收口径完成复核,不得只给口头结论。 " ) | Out-Null
$lines . Add ( " 3. 在复核结果回填模板中填写复核人、复核日期、复核结果、修改要求和证据路径。 " ) | Out-Null
$lines . Add ( " 4. 若结论为需要修改,必须写明修改位置、修改建议和是否阻断生产发布。 " ) | Out-Null
$lines . Add ( " 5. 回填后运行结果回填、进度、审计归档和总控门禁。 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " ## 验收口径 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " - 必做动作: $( $Order . required_action ) " ) | Out-Null
$lines . Add ( " - 回填产物: $( $Order . required_output ) " ) | Out-Null
$lines . Add ( " - 通过标准: $( $Order . acceptance_criteria ) " ) | Out-Null
$lines . Add ( " - 下一门禁: " + '``' + $Order . next_gate + '``' ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " ## 禁止项 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " 1. 不得把 PENDING 直接改成 APPROVED。 " ) | Out-Null
$lines . Add ( " 2. 不得使用脚本自动伪造复核人、复核日期或批准意见。 " ) | Out-Null
$lines . Add ( " 3. 没有真实证据路径时,不得进入生产发布。 " ) | Out-Null
$lines | Set-Content -Encoding UTF8 -Path $Path
}
function Write-AckWorkOrderMd {
param ( [ object ] $Order , [ string ] $Path )
$lines = New-Object System . Collections . Generic . List [ string ]
$lines . Add ( " # 阶段 13 交付签收任务单: $( $Order . work_order_id ) " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " ## 签收对象 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " | 字段 | 值 | " ) | Out-Null
$lines . Add ( " |---|---| " ) | Out-Null
$lines . Add ( " | 签收角色 | $( $Order . recipient_role ) | " ) | Out-Null
$lines . Add ( " | 接收人 | $( $Order . recipient ) | " ) | Out-Null
$lines . Add ( " | 交付负责人 | $( $Order . handoff_owner ) | " ) | Out-Null
$lines . Add ( " | 截止日期 | $( $Order . due_date ) | " ) | Out-Null
$lines . Add ( " | 签收材料 | $( $Order . package_path ) | " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " ## 签收要求 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " 1. 接收人确认已收到首批 P0 复核任务包。 " ) | Out-Null
$lines . Add ( " 2. 接收人确认资料可打开、任务数量和角色范围正确。 " ) | Out-Null
$lines . Add ( " 3. 如需补件,必须在签收记录中列明缺失材料。 " ) | Out-Null
$lines . Add ( " 4. 签收记录回填后重新运行复核进度门禁。 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " ## 验收标准 " ) | Out-Null
$lines . Add ( " " ) | Out-Null
$lines . Add ( " - `a ck_pending=0 ` 。 " ) | Out-Null
$lines . Add ( " - `a cknowledged_rows>0 ` 。 " ) | Out-Null
$lines . Add ( " - 签收文件中必须包含接收人、签收日期、材料路径和是否需要补件。 " ) | Out-Null
$lines | Set-Content -Encoding UTF8 -Path $Path
}
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 }
}
$selectedTasksPath = if ( [ string ] :: IsNullOrWhiteSpace ( $ReviewPacketPath ) ) { " " } else { Join-Path $ReviewPacketPath " selected-review-tasks.csv " }
$resultTemplatePath = if ( [ string ] :: IsNullOrWhiteSpace ( $ReviewPacketPath ) ) { " " } else { Join-Path $ReviewPacketPath " review-result-import-template.csv " }
$assignmentTemplatePath = if ( [ string ] :: IsNullOrWhiteSpace ( $ReviewPacketPath ) ) { " " } else { Join-Path $ReviewPacketPath " review-assignment-template.csv " }
$controlSummaryPath = if ( [ string ] :: IsNullOrWhiteSpace ( $ControlArtifactPath ) ) { " " } else { Join-Path $ControlArtifactPath " phase13-review-control-center-summary.json " }
$tasks = Import-CsvRows -Path $selectedTasksPath
$controlSummary = Read-JsonFile -Path $controlSummaryPath
if ( $tasks . Count -gt 0 ) {
Add-Check -Key " review_task_source " -Label " 首批复核任务来源 " -Status " pass " -Detail " 读取到 $( $tasks . Count ) 个首批复核任务 " -Meta @ { source = $selectedTasksPath }
} else {
Add-Check -Key " review_task_source " -Label " 首批复核任务来源 " -Status " fail " -Detail " 未读取到首批复核任务 " -Meta @ { source = $selectedTasksPath }
}
if ( $null -ne $controlSummary ) {
Add-Check -Key " control_center_source " -Label " 复核总控来源 " -Status " pass " -Detail " 读取到总控 blocker 矩阵来源,状态= $( $controlSummary . status ) " -Meta @ { source = $controlSummaryPath }
} else {
Add-Check -Key " control_center_source " -Label " 复核总控来源 " -Status " warn " -Required $false -Detail " 未读取到总控摘要;仍可基于首批复核任务生成任务包 " -Meta @ { source = $controlSummaryPath }
}
$legalAssignee = if ( [ string ] :: IsNullOrWhiteSpace ( $LegalReviewer ) ) { " 待指定真实法务复核人 " } else { $LegalReviewer }
$businessAssignee = if ( [ string ] :: IsNullOrWhiteSpace ( $BusinessReviewer ) ) { " 待指定真实业务复核人 " } else { $BusinessReviewer }
$handoffAssignee = if ( [ string ] :: IsNullOrWhiteSpace ( $HandoffOwner ) ) { " 待指定交付负责人 " } else { $HandoffOwner }
foreach ( $task in ( $tasks | Sort-Object priority , review_lane , asset_type , asset_id ) ) {
$legalWorkOrderPath = Join-Path $legalWorkOrderDir " $( $task . review_id ) .md "
$legalOrder = New-ReviewWorkOrder -Task $task -OrderType " legal_review " -Assignee $legalAssignee -AssigneeRole " 真实法务复核人 " -WorkOrderPath $legalWorkOrderPath
$legalRows . Add ( $legalOrder ) | Out-Null
Add-AcceptanceRow -Order $legalOrder -BlockerArea " legal_reviewer_assigned "
Write-ReviewWorkOrderMd -Order $legalOrder -Path $legalWorkOrderPath
if ( $task . review_lane -match " 业务 " ) {
$businessWorkOrderPath = Join-Path $businessWorkOrderDir " $( $task . review_id ) .md "
$businessOrder = New-ReviewWorkOrder -Task $task -OrderType " business_review " -Assignee $businessAssignee -AssigneeRole " 真实业务复核人 " -WorkOrderPath $businessWorkOrderPath
$businessRows . Add ( $businessOrder ) | Out-Null
Add-AcceptanceRow -Order $businessOrder -BlockerArea " business_reviewer_assigned "
Write-ReviewWorkOrderMd -Order $businessOrder -Path $businessWorkOrderPath
}
}
$ackSpecs = @ (
[ pscustomobject ] @ { ack_id = " ACK-LEGAL " ; recipient_role = " 法务接收人 " ; recipient = $legalAssignee ; blocker_area = " handoff_acknowledged " } ,
[ pscustomobject ] @ { ack_id = " ACK-BUSINESS " ; recipient_role = " 业务接收人 " ; recipient = $businessAssignee ; blocker_area = " handoff_acknowledged " }
)
foreach ( $ack in $ackSpecs ) {
$ackPath = Join-Path $ackWorkOrderDir " $( $ack . ack_id ) .md "
$ackStatus = Get-WorkOrderStatus -Assignee $ack . recipient -DueDateValue $DueDate
$ackRow = [ pscustomobject][ordered ] @ {
work_order_id = $ack . ack_id
order_type = " handoff_ack "
recipient_role = $ack . recipient_role
recipient = $ack . recipient
handoff_owner = $handoffAssignee
due_date = $DueDate
package_path = $ReviewPacketPath
required_output = " 填写并返回接收签收记录,确认收到复核任务包、材料可打开、缺失材料已列明 "
acceptance_criteria = " ack_pending=0 且 acknowledged_rows>0 "
evidence_target = " recipient-ack-template.csv 或等效签收记录 "
work_order_path = $ackPath
next_gate = " run-phase13-review-handoff-gate.ps1 -> run-phase13-review-progress-gate.ps1 -> run-phase13-review-control-center.ps1 "
status = $ackStatus
notes = " 未签收前不得视为真实复核已启动 "
}
$ackRows . Add ( $ackRow ) | Out-Null
$acceptanceRows . Add ( [ pscustomobject][ordered ] @ {
acceptance_id = " ACCEPT- $( $ackRow . work_order_id ) "
acceptance_type = $ackRow . order_type
blocker_area = $ack . blocker_area
review_id = " "
asset_id = " "
owner_role = $ackRow . recipient_role
assignee = $ackRow . recipient
due_date = $ackRow . due_date
required_output = $ackRow . required_output
acceptance_criteria = $ackRow . acceptance_criteria
evidence_target = $ackRow . evidence_target
current_status = $ackRow . status
next_gate = $ackRow . next_gate
} ) | Out-Null
Write-AckWorkOrderMd -Order $ackRow -Path $ackPath
}
$dispatchRows . Add ( [ pscustomobject][ordered ] @ {
package = " legal-review-work-orders "
owner = $legalAssignee
rows = $legalRows . Count
artifact = $legalPackageCsv
work_orders = $legalWorkOrderDir
acceptance = " 22 项法务复核全部完成并回填 "
} ) | Out-Null
$dispatchRows . Add ( [ pscustomobject][ordered ] @ {
package = " business-review-work-orders "
owner = $businessAssignee
rows = $businessRows . Count
artifact = $businessPackageCsv
work_orders = $businessWorkOrderDir
acceptance = " 18 项业务复核全部完成并回填 "
} ) | Out-Null
$dispatchRows . Add ( [ pscustomobject][ordered ] @ {
package = " handoff-ack-work-orders "
owner = $handoffAssignee
rows = $ackRows . Count
artifact = $ackPackageCsv
work_orders = $ackWorkOrderDir
acceptance = " 2 项签收全部完成 "
} ) | Out-Null
$legalRows | Export-Csv -LiteralPath $legalPackageCsv -NoTypeInformation -Encoding UTF8
$businessRows | Export-Csv -LiteralPath $businessPackageCsv -NoTypeInformation -Encoding UTF8
$ackRows | Export-Csv -LiteralPath $ackPackageCsv -NoTypeInformation -Encoding UTF8
$acceptanceRows | Export-Csv -LiteralPath $acceptanceBoardCsv -NoTypeInformation -Encoding UTF8
$dispatchRows | Export-Csv -LiteralPath $dispatchManifestCsv -NoTypeInformation -Encoding UTF8
$expectedLegalRows = [ int ] ( Get-MetricValue -Summary $controlSummary -Name " missing_legal_reviewer_tasks " -Default $tasks . Count )
$expectedBusinessRows = [ int ] ( Get-MetricValue -Summary $controlSummary -Name " missing_business_reviewer_tasks " -Default @ ( $tasks | Where-Object { $_ . review_lane -match " 业务 " } ) . Count )
$expectedAckRows = [ int ] ( Get-MetricValue -Summary $controlSummary -Name " ack_pending " -Default 2 )
if ( $legalRows . Count -eq $expectedLegalRows ) {
Add-Check -Key " legal_task_package " -Label " 法务复核任务包 " -Status " pass " -Detail " 已生成 $( $legalRows . Count ) 项法务复核工作单 "
} else {
Add-Check -Key " legal_task_package " -Label " 法务复核任务包 " -Status " fail " -Detail " 法务任务数量不匹配:actual= $( $legalRows . Count ) , expected= $expectedLegalRows "
}
if ( $businessRows . Count -eq $expectedBusinessRows ) {
Add-Check -Key " business_task_package " -Label " 业务复核任务包 " -Status " pass " -Detail " 已生成 $( $businessRows . Count ) 项业务复核工作单 "
} else {
Add-Check -Key " business_task_package " -Label " 业务复核任务包 " -Status " fail " -Detail " 业务任务数量不匹配:actual= $( $businessRows . Count ) , expected= $expectedBusinessRows "
}
if ( $ackRows . Count -eq $expectedAckRows ) {
Add-Check -Key " ack_task_package " -Label " 签收任务包 " -Status " pass " -Detail " 已生成 $( $ackRows . Count ) 项签收工作单 "
} else {
Add-Check -Key " ack_task_package " -Label " 签收任务包 " -Status " fail " -Detail " 签收任务数量不匹配:actual= $( $ackRows . Count ) , expected= $expectedAckRows "
}
$expectedAcceptanceRows = $legalRows . Count + $businessRows . Count + $ackRows . Count
if ( $acceptanceRows . Count -eq $expectedAcceptanceRows -and $acceptanceRows . Count -gt 0 ) {
Add-Check -Key " acceptance_tracking_board " -Label " 验收追踪看板 " -Status " pass " -Detail " 已生成 $( $acceptanceRows . Count ) 个原子验收项 "
} else {
Add-Check -Key " acceptance_tracking_board " -Label " 验收追踪看板 " -Status " fail " -Detail " 验收项数量异常:actual= $( $acceptanceRows . Count ) , expected= $expectedAcceptanceRows "
}
$missingLegalAssignee = @ ( $legalRows | Where-Object { Test-PendingValue -Value $_ . assignee } )
$missingBusinessAssignee = @ ( $businessRows | Where-Object { Test-PendingValue -Value $_ . assignee } )
$pendingAckRows = @ ( $ackRows | Where-Object { Test-PendingValue -Value $_ . recipient } )
$missingDueDateRows = @ ( $acceptanceRows | Where-Object { [ string ] :: IsNullOrWhiteSpace ( $_ . due_date ) } )
$readyOrders = @ ( $acceptanceRows | Where-Object { $_ . current_status -eq " READY_TO_DISPATCH " } )
$blockedOrders = @ ( $acceptanceRows | Where-Object { $_ . current_status -ne " READY_TO_DISPATCH " } )
if ( $missingLegalAssignee . Count -eq 0 ) {
Add-Check -Key " legal_assignee_ready " -Label " 法务复核人可派发 " -Status " pass " -Detail " 法务复核工作单均已指定真实复核人 "
} else {
Add-Check -Key " legal_assignee_ready " -Label " 法务复核人可派发 " -Status " blocked " -Detail " 仍有 $( $missingLegalAssignee . Count ) 项法务复核工作单缺少真实复核人 "
}
if ( $missingBusinessAssignee . Count -eq 0 ) {
Add-Check -Key " business_assignee_ready " -Label " 业务复核人可派发 " -Status " pass " -Detail " 业务复核工作单均已指定真实复核人 "
} else {
Add-Check -Key " business_assignee_ready " -Label " 业务复核人可派发 " -Status " blocked " -Detail " 仍有 $( $missingBusinessAssignee . Count ) 项业务复核工作单缺少真实复核人 "
}
if ( $pendingAckRows . Count -eq 0 ) {
Add-Check -Key " ack_recipient_ready " -Label " 签收接收人可派发 " -Status " pass " -Detail " 签收工作单均已指定接收人 "
} else {
Add-Check -Key " ack_recipient_ready " -Label " 签收接收人可派发 " -Status " blocked " -Detail " 仍有 $( $pendingAckRows . Count ) 项签收工作单缺少真实接收人 "
}
if ( $missingDueDateRows . Count -eq 0 ) {
Add-Check -Key " due_date_ready " -Label " 复核截止日期可执行 " -Status " pass " -Detail " 所有验收项均已设置截止日期 "
} else {
Add-Check -Key " due_date_ready " -Label " 复核截止日期可执行 " -Status " blocked " -Detail " 仍有 $( $missingDueDateRows . Count ) 个验收项缺少截止日期 "
}
$fabricatedApprovals = @ ( $acceptanceRows | Where-Object { $_ . current_status -match " APPROVED|PASS|DONE|COMPLETED " } )
if ( $fabricatedApprovals . Count -eq 0 ) {
Add-Check -Key " no_fabricated_review_result " -Label " 未伪造复核结论 " -Status " pass " -Detail " 任务包只生成待执行工作项,没有自动生成批准结论 "
} else {
Add-Check -Key " no_fabricated_review_result " -Label " 未伪造复核结论 " -Status " fail " -Detail " 发现疑似自动批准状态: $( $fabricatedApprovals . Count ) "
}
$guide = New-Object System . Collections . Generic . List [ string ]
$guide . Add ( " # 阶段 13 第十五批:复核任务包与验收追踪操作说明 " ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( " 本目录把第十四批总控 blocker 拆成可派发、可签收、可回填、可验收的原子工作项。 " ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( " ## 包含内容 " ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( '1. `legal-review-work-orders.csv`: 22 项法务复核工作单。' ) | Out-Null
$guide . Add ( '2. `business-review-work-orders.csv`: 18 项业务复核工作单。' ) | Out-Null
$guide . Add ( '3. `handoff-ack-work-orders.csv`: 2 项签收工作单。' ) | Out-Null
$guide . Add ( '4. `review-acceptance-tracking-board.csv`: 42 个原子验收项。' ) | Out-Null
$guide . Add ( '5. `work-orders/`:逐项 Markdown 任务单。' ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( " ## 执行顺序 " ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( " 1. 指定真实法务复核人、真实业务复核人、交付负责人和截止日期。 " ) | Out-Null
$guide . Add ( '2. 将 `work-orders/ack/` 交给接收人完成签收。' ) | Out-Null
$guide . Add ( '3. 将 `work-orders/legal/` 和 `work-orders/business/` 分别交给真实复核人。' ) | Out-Null
$guide . Add ( " 4. 复核人把结论写入首批复核结果回填模板。 " ) | Out-Null
$guide . Add ( " 5. 依次运行结果回填、进度、审计归档、总控和生产发布门禁。 " ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( " ## 推荐命令 " ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( '```powershell' ) | Out-Null
$guide . Add ( " powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-phase13-review-task-package-gate.ps1 -LegalReviewer <真实法务姓名> -BusinessReviewer <真实业务姓名> -HandoffOwner <交付负责人> -DueDate <YYYY-MM-DD> " ) | Out-Null
$guide . Add ( " powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-phase13-review-handoff-gate.ps1 -LegalReviewer <真实法务姓名> -BusinessReviewer <真实业务姓名> -HandoffOwner <交付负责人> -DueDate <YYYY-MM-DD> " ) | Out-Null
$guide . Add ( " powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-phase13-review-progress-gate.ps1 " ) | Out-Null
$guide . Add ( " powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts\run-phase13-review-control-center.ps1 " ) | Out-Null
$guide . Add ( '```' ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( " ## 硬性规则 " ) | Out-Null
$guide . Add ( " " ) | Out-Null
$guide . Add ( " 1. 没有真实复核人,不得把任务状态视为已启动。 " ) | Out-Null
$guide . Add ( " 2. 没有签收记录,不得把任务状态视为已交付。 " ) | Out-Null
$guide . Add ( " 3. 没有真实复核结论和证据路径,不得把资产状态推进到生产发布。 " ) | Out-Null
$guide | Set-Content -Encoding UTF8 -Path $operatorGuideMd
$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 ] @ {
selected_review_tasks = $tasks . Count
legal_review_work_orders = $legalRows . Count
business_review_work_orders = $businessRows . Count
ack_work_orders = $ackRows . Count
acceptance_tracking_rows = $acceptanceRows . Count
ready_to_dispatch_orders = $readyOrders . Count
blocked_orders = $blockedOrders . Count
missing_legal_assignee_orders = $missingLegalAssignee . Count
missing_business_assignee_orders = $missingBusinessAssignee . Count
ack_pending_orders = $pendingAckRows . Count
missing_due_date_acceptance_items = $missingDueDateRows . Count
expected_legal_review_work_orders = $expectedLegalRows
expected_business_review_work_orders = $expectedBusinessRows
expected_ack_work_orders = $expectedAckRows
control_blocker_count = [ int ] ( Get-MetricValue -Summary $controlSummary -Name " blocker_count " -Default 0 )
control_readiness_score = [ int ] ( Get-MetricValue -Summary $controlSummary -Name " readiness_score " -Default 0 )
control_readiness_total = [ int ] ( Get-MetricValue -Summary $controlSummary -Name " readiness_total " -Default 0 )
}
$summary = [ ordered ] @ {
name = " phase13-review-task-package-gate "
status = $overallStatus
generated_at = ( Get-Date ) . ToUniversalTime ( ) . ToString ( " o " )
branch = $branch
commit = $commit
remote = $remoteUrl
review_packet_path = $ReviewPacketPath
control_artifact_path = $ControlArtifactPath
checks = $checks . ToArray ( )
metrics = $metrics
artifacts = [ ordered ] @ {
summary_json = $summaryJson
summary_md = $summaryMd
legal_work_orders = $legalPackageCsv
business_work_orders = $businessPackageCsv
ack_work_orders = $ackPackageCsv
acceptance_tracking_board = $acceptanceBoardCsv
dispatch_manifest = $dispatchManifestCsv
operator_guide = $operatorGuideMd
work_orders_dir = $workOrderRoot
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 ( " - 首批复核包: $ReviewPacketPath " ) | Out-Null
$md . Add ( " - 总控证据: $ControlArtifactPath " ) | Out-Null
$md . Add ( " - 产物目录: $artifactDir " ) | 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 . selected_review_tasks ) | " ) | Out-Null
$md . Add ( " | 法务复核工作单 | $( $metrics . legal_review_work_orders ) | " ) | Out-Null
$md . Add ( " | 业务复核工作单 | $( $metrics . business_review_work_orders ) | " ) | Out-Null
$md . Add ( " | 签收工作单 | $( $metrics . ack_work_orders ) | " ) | Out-Null
$md . Add ( " | 原子验收项 | $( $metrics . acceptance_tracking_rows ) | " ) | Out-Null
$md . Add ( " | 可派发工作项 | $( $metrics . ready_to_dispatch_orders ) | " ) | Out-Null
$md . Add ( " | 阻塞工作项 | $( $metrics . blocked_orders ) | " ) | 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 ( " 第十五批已把总控 blocker 拆成可执行任务包,但仍缺少真实复核人、签收接收人或截止日期,因此只能进入派发准备状态,不能视为真实复核完成。 " ) | Out-Null
} elseif ( $overallStatus -eq " PASS " ) {
$md . Add ( " 第十五批任务包已具备派发条件,可以交给真实法务、业务和签收接收人执行。 " ) | Out-Null
} else {
$md . Add ( " 第十五批任务包未完全通过,请查看检查项和验收追踪看板。 " ) | Out-Null
}
$md . Add ( " " ) | Out-Null
$md . Add ( " 本地产物: " ) | Out-Null
$md . Add ( " " ) | Out-Null
$md . Add ( " - phase13-review-task-package-gate-summary.json " ) | Out-Null
$md . Add ( " - phase13-review-task-package-gate-summary.md " ) | Out-Null
$md . Add ( " - legal-review-work-orders.csv " ) | Out-Null
$md . Add ( " - business-review-work-orders.csv " ) | Out-Null
$md . Add ( " - handoff-ack-work-orders.csv " ) | Out-Null
$md . Add ( " - review-acceptance-tracking-board.csv " ) | Out-Null
$md . Add ( " - review-dispatch-manifest.csv " ) | Out-Null
$md . Add ( " - review-task-package-operator-guide.md " ) | Out-Null
$md . Add ( " - work-orders/ " ) | Out-Null
$md | Set-Content -Encoding UTF8 -Path $summaryMd
Write-Host " PHASE13_REVIEW_TASK_PACKAGE_GATE_ $overallStatus $artifactDir "
if ( $overallStatus -eq " FAIL " ) {
throw " 阶段 13 复核任务包与验收追踪门禁未通过。查看: $summaryMd "
}
if ( $overallStatus -eq " BLOCKED " -and $FailOnBlocked ) {
throw " 阶段 13 复核任务包与验收追踪门禁被阻塞。查看: $summaryMd "
}