# Structural verification (NOT part of the shipped app; under .preflight/, gitignored). # # Proves on disk -- not by prose -- that the two structural requirements are satisfied: # [1] The giant implementation files were split by responsibility into sibling files # under each component's own Src/ (and each new translation unit is compiled). # [2] The common UI controls live in a real `Components/Shared` component, are compiled # into the binary, and are consumed by the other components. # # Run: powershell -File .preflight\perf\structcheck.ps1 # ASCII only (PowerShell 5.1 reads .ps1 as ANSI). $ErrorActionPreference = "Stop" $root = Split-Path -Parent $PSScriptRoot Set-Location $root $pass = 0 $fail = 0 function Check([bool]$ok, [string]$what) { if ($ok) { $script:pass++ ; Write-Host (" OK " + $what) } else { $script:fail++ ; Write-Host (" FAIL " + $what) } } function Lines([string]$path) { if (-not (Test-Path $path)) { return -1 } return (Get-Content $path | Measure-Object -Line).Lines } Write-Host "== [1] split of the giant implementation files ==" # 1a. every expected file of the split exists and is a sibling under the component's Src/ $split = @( "Components/PlotView/Src/PlotView.cpp", "Components/PlotView/Src/PlotViewPaint.cpp", "Components/PlotView/Src/PlotViewInteraction.cpp", "Components/PlotView/Src/PlotViewFeatures.cpp", "Components/PlotView/Src/PlotViewInternal.hpp", "Components/SignalPanel/Src/SignalPanel.cpp", "Components/SignalPanel/Src/SignalPanelPaint.cpp", "Components/SignalPanel/Src/SignalPanelInteraction.cpp", "Components/SignalPanel/Src/SignalPanelInternal.hpp", "Components/Application/Src/Application.cpp", "Components/Application/Src/ApplicationCommands.cpp", "Components/Application/Src/ApplicationLoading.cpp", "Components/Application/Src/ApplicationInternal.hpp", "Utilities/Src/DataModel.cpp", "Utilities/Src/DataModelTab.cpp", "Utilities/Src/DataModelInternal.hpp" ) foreach ($f in $split) { $n = Lines $f Check ($n -gt 0) ("exists + non-empty: " + $f + " (" + $n + " lines)") } # 1b. the core file of each split is a small fraction of the original monster file Check ((Lines "Components/PlotView/Src/PlotView.cpp") -lt 900) "PlotView.cpp shrunk to < 900 lines (was 13 695)" Check ((Lines "Components/PlotView/Src/PlotViewPaint.cpp") -gt 4000) "PlotViewPaint.cpp carries the rendering (> 4000 lines)" Check ((Lines "Components/SignalPanel/Src/SignalPanel.cpp") -lt 1200) "SignalPanel.cpp shrunk to < 1200 lines (was 4 184)" Check ((Lines "Components/Application/Src/Application.cpp") -lt 1500) "Application.cpp shrunk to < 1500 lines (was 3 135)" Check ((Lines "Utilities/Src/DataModel.cpp") -lt 1600) "DataModel.cpp shrunk to < 1600 lines (was 2 615)" # 1c. responsibility really moved: each concern is defined in exactly one file $concerns = @( @{ symbol = "PlotView::Paint"; file = "PlotViewPaint.cpp" }, @{ symbol = "PlotView::PaintLane"; file = "PlotViewPaint.cpp" }, @{ symbol = "PlotView::PaintHint"; file = "PlotViewPaint.cpp" }, @{ symbol = "PlotView::LaneAtY"; file = "PlotViewInteraction.cpp" }, @{ symbol = "PlotView::CancelDrag"; file = "PlotViewInteraction.cpp" }, @{ symbol = "PlotView::FitAll"; file = "PlotViewFeatures.cpp" }, @{ symbol = "PlotView::SetHint"; file = "PlotViewFeatures.cpp" }, @{ symbol = "PlotView::Layout"; file = "PlotView.cpp" }, @{ symbol = "PlotView::BuildLaneVisuals"; file = "PlotView.cpp" }, @{ symbol = "SignalPanel::Paint"; file = "SignalPanelPaint.cpp" }, @{ symbol = "SignalPanel::OnLButtonDown"; file = "SignalPanelInteraction.cpp" }, @{ symbol = "SignalPanel::EnsureRows"; file = "SignalPanel.cpp" } ) foreach ($c in $concerns) { $hits = @(Select-String -Path "Components/*/Src/*.cpp", "Utilities/Src/*.cpp" -Pattern ("^\s*\w[\w:<>, ]* " + [regex]::Escape($c.symbol) + "\(") | ForEach-Object { Split-Path $_.Path -Leaf }) $ok = ($hits.Count -eq 1) -and ($hits[0] -eq $c.file) Check $ok ($c.symbol + " defined only in " + $c.file + " (found: " + ($hits -join ",") + ")") } # 1d. the constant/tool block of each big file became a named internal header + using-declaration $internal = @( @{ file = "Components/PlotView/Src/PlotViewInternal.hpp"; ns = "pvdetail" }, @{ file = "Components/SignalPanel/Src/SignalPanelInternal.hpp"; ns = "spdetail" }, @{ file = "Components/Application/Src/ApplicationInternal.hpp"; ns = "appdetail" }, @{ file = "Utilities/Src/DataModelInternal.hpp"; ns = "dmdetail" } ) foreach ($i in $internal) { $text = Get-Content $i.file -Raw $hasNs = $text -match ("namespace\s+" + $i.ns) $hasUsing = $text -match ("using namespace\s+" + $i.ns + "\s*;") $inlineCount = ([regex]::Matches($text, "(?m)^\s*(?:\[\[[^\]]*\]\]\s*)?inline\s")).Count Check $hasNs ($i.file + " declares namespace " + $i.ns) Check $hasUsing ($i.file + " injects it via the using-declaration for " + $i.ns) Check ($inlineCount -ge 4) ($i.file + " marks the shared helpers inline (" + $inlineCount + " definitions)") } # 1e. the split files are compiled, and their objects hold the moved code $objs = @("PlotView.o","PlotViewPaint.o","PlotViewInteraction.o","PlotViewFeatures.o", "SignalPanel.o","SignalPanelPaint.o","SignalPanelInteraction.o", "Application.o","ApplicationCommands.o","ApplicationLoading.o", "DataModel.o","DataModelTab.o") $missing = @() foreach ($o in $objs) { if (-not (Test-Path ("Build/Release/" + $o))) { $missing += $o } } Check ($missing.Count -eq 0) ("all split translation units were compiled (missing: " + ($missing -join ",") + ")") $nm = Get-Command nm -ErrorAction SilentlyContinue if ($nm -ne $null) { $paintInPaint = @(nm --defined-only -C "Build/Release/PlotViewPaint.o" 2>$null | Select-String "jdi::PlotView::Paint") $paintInCore = @(nm --defined-only -C "Build/Release/PlotView.o" 2>$null | Select-String "jdi::PlotView::Paint") Check ($paintInPaint.Count -gt 0) "nm: paint symbols live in PlotViewPaint.o" Check ($paintInCore.Count -eq 0) "nm: no paint symbol left in PlotView.o" } else { Check $false "nm not available (cannot verify symbol placement)" } # 1f. every implementation file of every component is registered in the Makefile $srcFiles = @(Get-ChildItem "Components/*/Src/*.cpp", "Utilities/Src/*.cpp" | ForEach-Object { $_.FullName.Substring($root.Length + 1).Replace("\", "/") }) $makefile = Get-Content "Makefile" -Raw $unregistered = @() foreach ($s in $srcFiles) { $dir = $s.Substring(0, $s.LastIndexOf("/")) if ($makefile -notmatch [regex]::Escape($dir)) { $unregistered += $s } } Check ($unregistered.Count -eq 0) ("every implementation directory is registered in the Makefile (missing: " + ($unregistered -join ",") + ")") Check ($srcFiles.Count -ge 24) ("implementation files in the tree: " + $srcFiles.Count) Write-Host "" Write-Host "== [2] Components/Shared ==" Check (Test-Path "Components/Shared/Inc/UiControls.hpp") "Components/Shared/Inc/UiControls.hpp exists" Check (Test-Path "Components/Shared/Src/UiControls.cpp") "Components/Shared/Src/UiControls.cpp exists" Check ((Lines "Components/Shared/Inc/UiControls.hpp") -gt 100) ("UiControls.hpp declares the control API (" + (Lines "Components/Shared/Inc/UiControls.hpp") + " lines)") # 2a. the shared header really owns the enumerated control families $families = @("UiDrawPlusIcon","UiDrawCrossIcon","UiDrawTriangleIcon","UiDrawCheckBox", "UiDrawSlider","UiDrawEditField","UiEditFieldMouseDown","UiDrawSwatch", "UiSwatchNeedsOutline","UiContrastInk","UiSliderRatioFromX") $header = Get-Content "Components/Shared/Inc/UiControls.hpp" -Raw $missingFamilies = @() foreach ($f in $families) { if ($header -notmatch ("\b" + $f + "\b")) { $missingFamilies += $f } } Check ($missingFamilies.Count -eq 0) ("shared header exposes all control families (missing: " + ($missingFamilies -join ",") + ")") # 2b. symbol-level proof: every declared control is DEFINED by UiControls.o and by no other object. # (Text matching cannot tell a definition from a call, so ask the linker's view instead.) $declared = @([regex]::Matches($header, "(?m)^\s*[\w:<>, ]+\s+(Ui\w+)\s*\(") | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique) Check ($declared.Count -ge 15) ("UiControls.hpp declares " + $declared.Count + " control functions") $definitionOwner = @{} $objectFiles = @(Get-ChildItem "Build/Release/*.o" -ErrorAction SilentlyContinue) if ($nm -eq $null) { Check $false "nm not available (cannot verify control definitions)" } else { foreach ($o in $objectFiles) { $syms = nm --defined-only -C $o.FullName 2>$null foreach ($name in $declared) { if ($syms | Select-String (" T jdi::" + [regex]::Escape($name) + "\(")) { $definitionOwner[$name] = $o.Name } } } $wrongOwner = @($declared | Where-Object { -not $definitionOwner.ContainsKey($_) -or $definitionOwner[$_] -ne "UiControls.o" }) Check ($wrongOwner.Count -eq 0) ("nm: all " + $declared.Count + " declared controls are defined in UiControls.o (wrong/undefined: " + ($wrongOwner -join ",") + ")") $uiControlsSymbols = @(nm --defined-only -C "Build/Release/UiControls.o" 2>$null | Select-String " T jdi::Ui").Count Check ($uiControlsSymbols -ge 15) ("nm: UiControls.o defines " + $uiControlsSymbols + " Ui* symbols") } # 2c. other components actually consume them (>= 3 components, >= 30 call sites) $consumers = @(Select-String -Path "Components/*/Src/*.cpp", "Components/*/Inc/*.hpp" -Pattern "UiDrawPlusIcon|UiDrawCrossIcon|UiDrawTriangleIcon|UiDrawCheckBox|UiDrawSlider|UiDrawEditField|UiEditFieldMouseDown|UiEditFieldMouseMove|UiDrawSwatch|UiSwatchNeedsOutline|UiContrastInk|UiSliderRatioFromX|UiEditTextLeft|UiEditOffsetAt" | Where-Object { (Split-Path $_.Path -Leaf) -ne "UiControls.hpp" } | Group-Object { ($_.Path.Substring($root.Length + 1) -split "[\\/]")[1] }) $componentNames = @($consumers | ForEach-Object { $_.Name }) $totalCalls = 0 foreach ($g in $consumers) { $totalCalls += $g.Count } Check ($componentNames.Count -ge 3) ("shared controls consumed by >= 3 components (" + ($componentNames -join ",") + ")") Check ($totalCalls -ge 30) ("shared control call sites across components: " + $totalCalls) # 2d. layering, stated exactly as the code obeys it: # Components/Shared -> Utilities only (it must not include any component header) # Utilities/PaintUtil = base layer (must not include UiControls.hpp) # Utilities must not include feature components (PlotView / SignalPanel / Toolbar / TabBar) # NOTE: Utilities/Src/PopupMenu.cpp and Utilities/Src/ColorPickerPopup.cpp are hand-drawn UI # widgets that live in the Utilities folder historically; they consume the shared controls # (same direction as the components do) and are deliberately excluded from the ban below. $sharedIncludes = @(Select-String -Path "Components/Shared/Inc/*.hpp", "Components/Shared/Src/*.cpp" -Pattern '^#include "' | Where-Object { $_.Line -match '"(Application|PlotView|SignalPanel|Toolbar|TabBar|PopupMenu|ColorPickerPopup)\.hpp"' }) Check ($sharedIncludes.Count -eq 0) "Components/Shared depends on Utilities only (no component header)" $paintUtilIncludes = @(Select-String -Path "Utilities/Inc/PaintUtil.hpp", "Utilities/Src/PaintUtil.cpp" -Pattern '#include "UiControls.hpp"' -ErrorAction SilentlyContinue) Check ($paintUtilIncludes.Count -eq 0) "Utilities/PaintUtil (base layer) does not depend on Components/Shared" $badDir = @(Select-String -Path "Utilities/Src/*.cpp", "Utilities/Inc/*.hpp" -Pattern '#include "(PlotView|SignalPanel|Toolbar|TabBar)' -ErrorAction SilentlyContinue) Check ($badDir.Count -eq 0) "Utilities never includes a feature component" Write-Host "" Write-Host "== [3] user-requested behaviour invariants (source level) ==" $utf8 = New-Object System.Text.UTF8Encoding($false) $panelFile = "Components/SignalPanel/Src/SignalPanel.cpp" $panelIntFile = "Components/SignalPanel/Src/SignalPanelInteraction.cpp" $modelFile = "Utilities/Inc/DataModel.hpp" $panelText = [System.IO.File]::ReadAllText($panelFile, $utf8) $panelIntText = [System.IO.File]::ReadAllText($panelIntFile, $utf8) $modelText = [System.IO.File]::ReadAllText($modelFile, $utf8) # 3a. collapsing a group needs the disclosure triangle OR the "Group N" label -- clicking the name # itself must not collapse it. (User 2026-10-16.) Check ($panelText -match "return RowPart::GROUP_TITLE;") "hit test yields a dedicated GROUP_TITLE part" Check ($panelText -match "const gd::RectF labelRect\(labelLeft, rect\.Y, GroupLabelWidth\(row\), rect\.Height\);") "GROUP_TITLE hot zone is exactly the Group-N label rect" $gate = @(Select-String -Path $panelIntFile -Pattern "^\s*if \(part != RowPart::DISCLOSURE && part != RowPart::GROUP_TITLE\)") Check ($gate.Count -eq 1) ("single-click toggle gate accepts only DISCLOSURE / GROUP_TITLE (found " + $gate.Count + ")") $toggles = @(Select-String -Path $panelIntFile -Pattern "^\s*ToggleGroupFromRow\(item\.groupIndex, row\);") Check ($toggles.Count -eq 2) ("ToggleGroupFromRow has exactly 2 entry points, single click + double click (found " + $toggles.Count + ")") Check ($panelIntText -match "(?s)if \(part == RowPart::DISCLOSURE\)\s*\{\s*//[^\n]*\n(\s*//[^\n]*\n)*\s*ToggleGroupFromRow") "double click on the triangle still collapses" Check ($panelIntText -match "(?s)if \(part != RowPart::BODY\)\s*\{\s*return false;\s*\}") "double click outside the name body never starts a rename" # 3b. the group rename box must stop before the "N items" counter. (User 2026-10-16.) Check ($panelText -match "right = statsRight - statsWidth - 6\.0f \* m_scale;") "rename box right edge excludes the item counter" Check ($panelText -notmatch "right = statsRight;") "rename box never extends over the counter" Check ($panelText -match "const float statsWidth = UiMeasureTextWidth\(m_fonts->uiSmall, GroupStatsText\(row\)\);") "counter width comes from GroupStatsText" # 3c. SignalData / SignalGroup keep exactly ONE name field. (User 2026-10-16: drop displayName.) $data = [regex]::Match($modelText, "(?s)struct SignalData\s*\{(.*?)\n\t\};") Check ($data.Success) "SignalData struct found in the data model" if ($data.Success) { $body = $data.Groups[1].Value $fields = @([regex]::Matches($body, "std::wstring\s+\w+;")) Check ($fields.Count -eq 1) ("SignalData declares exactly one string field (found " + $fields.Count + ")") Check ($body -match "std::wstring name;") "that single field is name" } $group = [regex]::Match($modelText, "(?s)struct SignalGroup\s*\{(.*?)\n\t\};") Check ($group.Success) "SignalGroup struct found in the data model" if ($group.Success) { $gbody = $group.Groups[1].Value $gfields = @([regex]::Matches($gbody, "std::wstring\s+\w+;")) Check ($gfields.Count -eq 1) ("SignalGroup declares exactly one string field (found " + $gfields.Count + ")") } Check ($modelText -match "bool RenameSignal\(") "DataSet::RenameSignal replaces the old SetSignalDisplayName" Check ($modelText -match "bool RenameGroup\(") "DataSet::RenameGroup replaces the old SetGroupDisplayName" $staleNames = @(Select-String -Path "Components/*/Src/*.cpp","Components/*/Inc/*.hpp","Components/*/Src/*.hpp", "Utilities/Src/*.cpp","Utilities/Inc/*.hpp" -Pattern "displayName|SetSignalDisplayName|SetGroupDisplayName|\.Title\(\)|->Title\(\)") Check ($staleNames.Count -eq 0) ("no displayName / Title() left in any source (found " + $staleNames.Count + ")") # 3d. comment hygiene, driven by a UTF-8 data file (the .ps1 itself must stay ASCII for PowerShell 5.1) $phraseFile = "Tools/structcheck_phrases.txt" Check (Test-Path $phraseFile) "comment-hygiene phrase list exists" if (Test-Path $phraseFile) { $scanFiles = @(Get-ChildItem "Components/*/Src/*.cpp","Components/*/Src/*.hpp","Components/*/Inc/*.hpp", "Utilities/Src/*.cpp","Utilities/Inc/*.hpp" | ForEach-Object { $_.FullName }) $scanText = @{} foreach ($f in $scanFiles) { $scanText[$f] = [System.IO.File]::ReadAllText($f, $utf8) } foreach ($line in [System.IO.File]::ReadAllLines($phraseFile, $utf8)) { if ([string]::IsNullOrWhiteSpace($line)) { continue } $parts = $line -split "`t" $mode = $parts[0] if ($mode -eq "forbid") { $phrase = $parts[1] $offenders = @($scanFiles | Where-Object { $scanText[$_].Contains($phrase) }) Check ($offenders.Count -eq 0) ("stale wording absent everywhere: " + $phrase + " (found in " + $offenders.Count + " files)") } elseif ($mode -eq "require") { $target = $parts[1] $phrase = $parts[2] Check ((Test-Path $target) -and ([System.IO.File]::ReadAllText($target, $utf8).Contains($phrase))) ("corrected comment present in " + $target + ": " + $phrase) } } } Write-Host "" Write-Host ("structcheck: " + $pass + " passed, " + $fail + " failed") if ($fail -ne 0) { exit 1 } exit 0