Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
emit kit_manager_web '^apps/kit-manager-web/'
emit compose_config '^(compose\..*\.yml|\.env.*\.example|infra/docker/)'
emit powershell_static '^(\.github/workflows/|\.claude/workflows/|scripts/.*\.(ps1|psm1)$|AGENTS\.md|docs/agents/)'
emit rebuild_test_deploy '^scripts/(deploy\.ps1|dev/rebuild-test-deploy\.ps1|lib/(rebuild-test-deploy|design-assets|host-native-launcher)\.ps1|lib/StructLog\.psm1|tests/(test-rebuild-test-deploy|test-helpers)\.ps1)$'
emit rebuild_test_deploy '^scripts/(deploy\.ps1|dev/rebuild-test-deploy\.ps1|lib/(rebuild-test-deploy|design-assets|host-native-launcher)\.ps1|lib/StructLog\.psm1|tests/(test-rebuild-test-deploy|test-verify-all|test-helpers)\.ps1)$'
echo "secret_pattern_scan=true" >> "$GITHUB_OUTPUT"

- name: Classify frontend design scope from base and head manifests
Expand Down Expand Up @@ -561,6 +561,11 @@ jobs:
shell: pwsh
run: powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File scripts/tests/test-rebuild-test-deploy.ps1

- name: Run aggregate verifier profile tests
if: needs.changes.outputs.rebuild_test_deploy == 'true'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run verifier tests when verifier scripts change

With this condition, the new aggregate verifier tests only run when rebuild_test_deploy is true, but the classifier pattern above does not include scripts/verify-all.ps1 or scripts/verify-all.sh—only deploy/rebuild helpers and test files. A PR that changes the verifier itself will therefore get only static analysis and skip the regression tests added here; gate this step on a classifier that also matches the verifier scripts.

Useful? React with 👍 / 👎.

shell: pwsh
run: pwsh -NoProfile -NonInteractive -ExecutionPolicy Bypass -File scripts/tests/test-verify-all.ps1

secret-pattern-scan:
name: secret pattern scan
needs: changes
Expand Down
59 changes: 57 additions & 2 deletions scripts/lib/rebuild-test-deploy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,56 @@ function Invoke-TestDeployGitCommand {
return $result
}

function Get-TestDeployPruningContract {
[CmdletBinding()]
param()

return [pscustomobject]@{
RootToolingDirNames = @($script:TestDeployRootToolingDirNames)
PreservedProductionFiles = @($script:TestDeployPreservedProductionFiles)
}
}

function Get-TestDeployWindowsPowerShellChildEnvironment {
[CmdletBinding()]
param()

$systemRoot = [Environment]::GetEnvironmentVariable('SystemRoot', 'Machine')
if ([string]::IsNullOrWhiteSpace($systemRoot)) {
$systemRoot = [Environment]::GetEnvironmentVariable('SystemRoot', 'Process')
}
$programFiles = [Environment]::GetEnvironmentVariable('ProgramFiles', 'Machine')
if ([string]::IsNullOrWhiteSpace($programFiles)) {
$programFiles = [Environment]::GetEnvironmentVariable('ProgramFiles', 'Process')
}
$programFilesX86 = [Environment]::GetEnvironmentVariable('ProgramFiles(x86)', 'Machine')
if ([string]::IsNullOrWhiteSpace($programFilesX86)) {
$programFilesX86 = [Environment]::GetEnvironmentVariable('ProgramFiles(x86)', 'Process')
}

$candidateRoots = @()
if (-not [string]::IsNullOrWhiteSpace($systemRoot)) {
$candidateRoots += Join-Path $systemRoot 'System32\WindowsPowerShell\v1.0\Modules'
}
if (-not [string]::IsNullOrWhiteSpace($programFiles)) {
$candidateRoots += Join-Path $programFiles 'WindowsPowerShell\Modules'
}
if (-not [string]::IsNullOrWhiteSpace($programFilesX86)) {
$candidateRoots += Join-Path $programFilesX86 'WindowsPowerShell\Modules'
}

$moduleRoots = @(
$candidateRoots |
Where-Object { Test-Path -LiteralPath $_ -PathType Container } |
Select-Object -Unique
)
if ($moduleRoots.Count -eq 0) {
throw 'Windows PowerShell module roots are unavailable for the deployment child process.'
}

return @{ PSModulePath = ($moduleRoots -join [IO.Path]::PathSeparator) }
}

function Invoke-TestDeployScript {
[CmdletBinding()]
param(
Expand All @@ -745,8 +795,13 @@ function Invoke-TestDeployScript {
)

$arguments = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', 'scripts\deploy.ps1', '-Build')
$childEnvironment = Get-TestDeployWindowsPowerShellChildEnvironment
if ($null -ne $ProcessRunner) {
$exitCode = & $ProcessRunner -FilePath 'powershell.exe' -ArgumentList $arguments -WorkingDirectory $DeploymentRoot
$exitCode = & $ProcessRunner `
-FilePath 'powershell.exe' `
-ArgumentList $arguments `
-WorkingDirectory $DeploymentRoot `
-Environment $childEnvironment
} else {
function ConvertTo-CmdQuotedArgument {
param([Parameter(Mandatory = $true)][string] $Value)
Expand All @@ -766,7 +821,7 @@ function Invoke-TestDeployScript {
# with file redirection so long-lived child processes cannot keep the agent
# harness pipe open; then wait only for the direct cmd.exe process.
$quotedArgs = ($arguments | ForEach-Object { ConvertTo-CmdQuotedArgument -Value $_ }) -join ' '
$cmdLine = "powershell.exe $quotedArgs > $(ConvertTo-CmdQuotedArgument -Value $stdoutPath) 2> $(ConvertTo-CmdQuotedArgument -Value $stderrPath)"
$cmdLine = 'set "PSModulePath=' + $childEnvironment.PSModulePath + '" && powershell.exe ' + $quotedArgs + ' > ' + (ConvertTo-CmdQuotedArgument -Value $stdoutPath) + ' 2> ' + (ConvertTo-CmdQuotedArgument -Value $stderrPath)
$process = Start-Process -FilePath 'cmd.exe' `
-ArgumentList @('/c', $cmdLine) `
-WorkingDirectory $DeploymentRoot `
Expand Down
63 changes: 58 additions & 5 deletions scripts/tests/test-rebuild-test-deploy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,8 @@ try {
'tampered' | Set-Content -LiteralPath (Join-Path $assetStageRoot 'web-viewer-sample\public\design-assets\vp-test.png') -Encoding ascii
Assert-Throws { Sync-DeploymentDesignAssets -RepoRoot $assetStageRoot } 'tampered prestaged asset is rejected by SHA-256 manifest'

$resolveTestReparseTargetPath = ${function:Resolve-TestReparseTargetPath}
$assertTrue = ${function:Assert-True}
$removeVerifiedAssetJunction = {
param(
[Parameter(Mandatory = $true)][string] $LinkPath,
Expand All @@ -509,7 +511,7 @@ try {
if (-not [bool]($linkItem.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
throw "design asset test cleanup refused non-reparse path: '$LinkPath'"
}
$resolvedTarget = Resolve-TestReparseTargetPath -LinkItem $linkItem
$resolvedTarget = & $resolveTestReparseTargetPath -LinkItem $linkItem
if (
[string]::IsNullOrWhiteSpace($resolvedTarget) -or
-not $resolvedTarget.Equals(
Expand All @@ -520,8 +522,8 @@ try {
throw "design asset test cleanup refused junction target mismatch: '$LinkPath'"
}
[System.IO.Directory]::Delete([System.IO.Path]::GetFullPath($LinkPath), $false)
Assert-True (-not (Test-Path -LiteralPath $LinkPath)) 'design asset test junction is detached without traversing target'
Assert-True (Test-Path -LiteralPath $ExpectedTarget -PathType Container) 'design asset test junction target remains after detach'
& $assertTrue (-not (Test-Path -LiteralPath $LinkPath)) 'design asset test junction is detached without traversing target'
& $assertTrue (Test-Path -LiteralPath $ExpectedTarget -PathType Container) 'design asset test junction target remains after detach'
}.GetNewClosure()

$sourceGuardRoot = Join-Path $sandbox 'design-assets-source-guard'
Expand Down Expand Up @@ -1531,7 +1533,7 @@ exit 7
New-Item -ItemType Directory -Path $deployHelperRoot -Force | Out-Null
$deployHelperProbes = New-Object 'System.Collections.Generic.List[object]'
$deployHelperRunner = {
param([string] $FilePath, [string[]] $ArgumentList, [string] $WorkingDirectory)
param([string] $FilePath, [string[]] $ArgumentList, [string] $WorkingDirectory, [hashtable] $Environment)
$deployHelperProbes.Add([pscustomobject]@{
FilePath = $FilePath
ArgumentList = @($ArgumentList)
Expand All @@ -1547,8 +1549,59 @@ exit 7
Assert-Equal $deployHelperRoot $deployHelperProbe.WorkingDirectory 'deploy helper runs inside deployment root'
Assert-True (($deployHelperProbe.ArgumentList -join ' ') -match 'scripts\\deploy\.ps1') 'deploy helper invokes scripts\deploy.ps1'
Assert-True ($deployHelperProbe.ArgumentList -contains '-Build') 'deploy helper preserves -Build'

$realChildDeployRoot = Join-Path $sandbox 'real-child-module-path'
New-Item -ItemType Directory -Path (Join-Path $realChildDeployRoot 'scripts') -Force | Out-Null
@'
$hashCommand = Get-Command Get-FileHash -ErrorAction SilentlyContinue
if ($null -eq $hashCommand) {
exit 42
}
exit 0
'@ | Set-Content -LiteralPath (Join-Path $realChildDeployRoot 'scripts\deploy.ps1') -Encoding ascii

$contaminatedParentModulePath = 'C:\Program Files\PowerShell\7\Modules;C:\Users\operator\Documents\PowerShell\Modules'
$originalParentModulePath = [Environment]::GetEnvironmentVariable('PSModulePath', 'Process')
try {
[Environment]::SetEnvironmentVariable('PSModulePath', $contaminatedParentModulePath, 'Process')
$env:PSModulePath = $contaminatedParentModulePath

$realChildResult = Invoke-TestDeployScript -DeploymentRoot $realChildDeployRoot
Assert-Equal 0 $realChildResult.ExitCode 'Windows PowerShell child resolves Get-FileHash under a PowerShell 7-first parent module path'
Assert-Equal $contaminatedParentModulePath ([Environment]::GetEnvironmentVariable('PSModulePath', 'Process')) 'real Windows PowerShell child does not mutate the PowerShell 7-first parent environment'

$childEnvironmentProbe = New-Object 'System.Collections.Generic.List[object]'
$childEnvironmentRunner = {
param(
[string] $FilePath,
[string[]] $ArgumentList,
[string] $WorkingDirectory,
[hashtable] $Environment
)
$childEnvironmentProbe.Add([pscustomobject]@{
FilePath = $FilePath
ArgumentList = @($ArgumentList)
WorkingDirectory = $WorkingDirectory
Environment = $Environment
}) | Out-Null
return 0
}.GetNewClosure()

$childEnvironmentResult = Invoke-TestDeployScript -DeploymentRoot $deployHelperRoot -ProcessRunner $childEnvironmentRunner
Assert-Equal 0 $childEnvironmentResult.ExitCode 'deploy helper preserves its direct process result under a PowerShell 7-first parent'
Assert-Equal 1 $childEnvironmentProbe.Count 'deploy helper exposes one normalized child environment to the injected process runner'
$childModulePath = [string]$childEnvironmentProbe[0].Environment['PSModulePath']
Assert-True (-not [string]::IsNullOrWhiteSpace($childModulePath)) 'deploy helper provides PSModulePath only to its Windows PowerShell child'
Assert-True ($childModulePath -match '(?i)WindowsPowerShell') 'deploy helper child PSModulePath includes Windows PowerShell module roots'
Assert-True ($childModulePath -notmatch '(?i)[\\/]PowerShell[\\/]7[\\/]Modules') 'deploy helper child PSModulePath excludes PowerShell 7 module roots'
Assert-Equal $contaminatedParentModulePath ([Environment]::GetEnvironmentVariable('PSModulePath', 'Process')) 'deploy helper does not mutate the PowerShell 7-first parent environment'
} finally {
[Environment]::SetEnvironmentVariable('PSModulePath', $originalParentModulePath, 'Process')
$env:PSModulePath = $originalParentModulePath
}

$nullExitResult = Invoke-TestDeployScript -DeploymentRoot $deployHelperRoot -ProcessRunner {
param([string] $FilePath, [string[]] $ArgumentList, [string] $WorkingDirectory)
param([string] $FilePath, [string[]] $ArgumentList, [string] $WorkingDirectory, [hashtable] $Environment)
return $null
}
Assert-Equal 1 $nullExitResult.ExitCode 'deploy helper treats missing exit code as failure'
Expand Down
93 changes: 93 additions & 0 deletions scripts/tests/test-verify-all.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# scripts/tests/test-verify-all.ps1
# Verifies the canonical aggregate verifier's developer and pruned-deployment profiles.
Comment on lines +1 to +2

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
$verifyScript = Join-Path $repoRoot 'scripts\verify-all.ps1'
. (Join-Path $PSScriptRoot 'test-helpers.ps1')

function Invoke-VerificationPlan {
param(
[Parameter(Mandatory = $true)][string] $Profile,
[Parameter(Mandatory = $true)][string] $RepoRoot
)

$output = @(& (Get-Command pwsh -ErrorAction Stop).Source -NoProfile -NonInteractive -ExecutionPolicy Bypass -File $verifyScript `
-Profile $Profile -PlanOnly -RepoRoot $RepoRoot 2>&1)
return [pscustomobject]@{
ExitCode = $LASTEXITCODE
Output = ($output -join "`n")
}
}

$sandbox = New-TestSandbox -Prefix 'verify-all-profile'
try {
$developerPlan = Invoke-VerificationPlan -Profile 'Developer' -RepoRoot $repoRoot
Assert-Equal 0 $developerPlan.ExitCode 'default developer profile plan is available'
Assert-True ($developerPlan.Output -match '\[PLAN\] profile=developer') 'default profile identifies itself as developer'
foreach ($target in @(
'tests \(contracts\+fakes\)',
'bim-review-coordinator',
'web-viewer-sample',
'bim-streaming-server'
)) {
Assert-True ($developerPlan.Output -match "\[EXECUTE\] $target") "developer profile retains complete target '$target'"
}
Assert-True ($developerPlan.Output -notmatch '\[OMIT\]') 'developer profile does not omit its normal contract targets'

$deploymentRoot = Join-Path $sandbox 'pruned-deployment'
foreach ($directory in @(
'scripts',
'docs\plans',
'bim-review-coordinator',
'web-viewer-sample',
'bim-streaming-server\scripts\tests'
)) {
New-Item -ItemType Directory -Path (Join-Path $deploymentRoot $directory) -Force | Out-Null
}
'deploy entrypoint' | Set-Content -LiteralPath (Join-Path $deploymentRoot 'scripts\deploy.ps1') -Encoding ascii
'retained production design token' | Set-Content -LiteralPath (Join-Path $deploymentRoot 'docs\plans\ai-bim-governance.css') -Encoding ascii
'streaming contract entrypoint' | Set-Content -LiteralPath (Join-Path $deploymentRoot 'bim-streaming-server\scripts\tests\test-stage-loading-contract.ps1') -Encoding ascii

$deploymentPlan = Invoke-VerificationPlan -Profile 'Deployment' -RepoRoot $deploymentRoot
Assert-Equal 0 $deploymentPlan.ExitCode 'deployment profile accepts the intentionally pruned fixture inventory'
Assert-True ($deploymentPlan.Output -match '\[PLAN\] profile=deployment') 'deployment profile identifies itself explicitly'
foreach ($target in @(
'deployment required artifacts',
'coordinator health',
'governance health',
'conversion health',
'kit manager health',
'viewer endpoint'
)) {
Assert-True ($deploymentPlan.Output -match "\[EXECUTE\] $target") "deployment profile executes retained target '$target'"
}
foreach ($target in @(
'tests \(contracts\+fakes\)',
'bim-review-coordinator \(full verify\)',
'web-viewer-sample \(full verify\)',
'bim-streaming-server stage-loading contract'
)) {
Assert-True ($deploymentPlan.Output -match "\[OMIT\] $target") "deployment profile explicitly records authoring-only omission '$target'"
}

$verifyShell = Get-Content -LiteralPath (Join-Path $repoRoot 'scripts\verify-all.sh') -Raw
Assert-True ($verifyShell -match '--profile') 'POSIX verifier mirror accepts an explicit deployment profile'
Assert-True ($verifyShell -match '--plan-only') 'POSIX verifier mirror publishes the same profile inventory without executing it'
Comment on lines +76 to +78

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
git ls-files | rg '(^|/)test-verify-all\.ps1$|verify-all\.sh$|verify-all\.ps1$|AGENTS\.md$' || true

echo "== script sizes =="
for f in scripts/tests/test-verify-all.ps1 scripts/verify-all.sh scripts/verify-all.ps1 scripts/lib/StructLog.psm1; do
  [ -f "$f" ] && wc -l "$f"
done

echo "== inspect test file around assertions =="
if [ -f scripts/tests/test-verify-all.ps1 ]; then
  cat -n scripts/tests/test-verify-all.ps1 | sed -n '1,140p'
fi

echo "== inspect shell verifier outline/content =="
if [ -f scripts/verify-all.sh ]; then
  wc -l scripts/verify-all.sh
  cat -n scripts/verify-all.sh | sed -n '1,260p'
fi

echo "== inspect powershell verifier outline/content around relevant sections =="
if [ -f scripts/verify-all.ps1 ]; then
  wc -l scripts/verify-all.ps1
  cat -n scripts/verify-all.ps1 | sed -n '1,260p'
fi

echo "== search for test-verify-all usages and fixtures =="
rg -n "test-verify-all|verify-all|--profile|--plan-only|deployment profile|profile inventory|Plan output|missing|artifact" scripts AGENTS.md .github 2>/dev/null || true

Repository: monkey1sai/AI-BIM-governance

Length of output: 50385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== git status/stat =="
git status --short
git diff --stat

echo "== shell syntax parse if available =="
bash -n scripts/verify-all.sh 2>&1 || true
pwsh -Command "& {if (Test-Path scripts/verify-all.ps1) { $f='scripts/verify-all.ps1'; $tokens=$errors=$null; [System.Management.Automation.Parser]::ParseInput((Get-Content -Raw $f), [ref]$tokens, [ref]$errors); $errors?.Write(); $tokens } else { 'verify-all.ps1 missing' } }" 2>&1 || true

echo "== deterministic flag-token semantic probe =="
python3 - <<'PY'
from pathlib import Path
import re
p=Path('scripts/tests/test-verify-all.ps1')
if p.exists():
    text=p.read_text()
    print("has profile token assertion:", bool(re.search(r'--profile', text)))
    print("has plan-only token assertion:", bool(re.search(r'--plan-only', text)))
    print("calls verify shell exec:", bool(re.search(r'Invoke-Expression|Start-Process|powershell|pwsh|verify-all', text)))
    print("contains parity output assertions:", bool(re.search(r'parity|output|assert.*profile|profile inventory', text, re.I)))
PY

Repository: monkey1sai/AI-BIM-governance

Length of output: 261


Exercise the Bash profile contract instead of scanning tokens.

These assertions pass if the flags only occur as strings; they do not verify Bash parsing, the Deployment inventory generated from the pruned fixture, omitted targets, or missing-artifact failure. Run scripts/verify-all.sh with --profile Deployment --plan-only and assert parity with the PowerShell plan output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-verify-all.ps1` around lines 76 - 78, Replace the
token-matching assertions in the POSIX verifier checks with an actual invocation
of scripts/verify-all.sh using --profile Deployment --plan-only against the
pruned fixture. Capture and validate its parsed Deployment inventory against the
PowerShell plan output, including omitted targets, and assert that missing
artifacts produce the expected failure.

Source: Coding guidelines


Remove-Item -LiteralPath (Join-Path $deploymentRoot 'docs\plans\ai-bim-governance.css') -Force
$missingArtifactPlan = Invoke-VerificationPlan -Profile 'Deployment' -RepoRoot $deploymentRoot
Assert-True ($missingArtifactPlan.ExitCode -ne 0) 'deployment profile fails when a production-required retained artifact is missing'
Assert-True ($missingArtifactPlan.Output -match 'deployment required artifact missing') 'deployment profile reports the missing production-required artifact'

Write-TestPass 'verify-all profiles'
} catch {
Write-TestFail 'verify-all profiles' $_.Exception.Message
throw
} finally {
Remove-TestSandbox -Path $sandbox
}

exit 0
Loading
Loading