[CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$RepositoryRoot, [Parameter(Mandatory = $false)] [string]$OutputPath ) $ErrorActionPreference = 'Stop' if (-not $RepositoryRoot) { $RepositoryRoot = Split-Path -Parent $PSScriptRoot } $RepositoryRoot = [IO.Path]::GetFullPath($RepositoryRoot) $commandsPath = Join-Path $RepositoryRoot 'audits\p-rec-annot-v1.0.4\evidence\commands.csv' if (-not (Test-Path -LiteralPath $commandsPath -PathType Leaf)) { throw "CAPABILITY_LEDGER_COMMAND_SOURCE_MISSING: $commandsPath" } if (-not $OutputPath) { $OutputPath = Join-Path $RepositoryRoot 'capability-ledger\plant3d-p-rec-annot-commands.json' } $OutputPath = [IO.Path]::GetFullPath($OutputPath) $accepted = @{ 'PXP-AAV' = @{ Component = 'PRecAnnot.Plant3D2026'; Evidence = 'Accepted runtime session 7402fee6290b44438642f9204e8bdb6c; automation journal 0cf74ff1c90d4c69a10417904638c7b6'; Decision = 'MIGRATED_RUNTIME_VERIFIED' } 'PXP-OA' = @{ Component = 'PRecAnnot.Plant3D2026'; Evidence = 'Accepted runtime session 7402fee6290b44438642f9204e8bdb6c; automation journal 0cf74ff1c90d4c69a10417904638c7b6'; Decision = 'MIGRATED_RUNTIME_VERIFIED' } } $commands = Import-Csv -LiteralPath $commandsPath | Where-Object { $_.File -notmatch '(^|\\)_archive(\\|$)' } | Group-Object Command | ForEach-Object { $_.Group | Select-Object -First 1 } | Sort-Object Command $capabilities = foreach ($command in $commands) { $acceptedRecord = $accepted[$command.Command] $status = if ($acceptedRecord) { 'RUNTIME_VERIFIED' } else { 'MIGRATION_PLANNED' } [ordered]@{ id = ('PLANT3D-2019-CMD-' + ($command.Command.ToUpperInvariant() -replace '[^A-Z0-9]+', '-').Trim('-')) sourceProduct = 'Autodesk Plant 3D 2019' sourceComponent = $command.File userPurpose = 'Registered AutoCAD command; command body and workflow require source-specific acceptance.' launchMethod = ('AutoCAD command: ' + $command.Command) dependencies = @('Plant 3D drawing', 'P_Rec_Annot legacy module') resources = @('Project configuration', 'Drawing and annotation resources') projectConstraints = ('Plant project: ' + $command.PlantProject + '; reads drawing: ' + $command.ReadsDrawing + '; writes drawing: ' + $command.WritesDrawing) legacyStatus = 'ACTIVE_LEGACY' targetComponent = if ($acceptedRecord) { $acceptedRecord.Component } else { 'UNASSIGNED' } owner = if ($acceptedRecord) { 'Plant annotation domain owner' } else { 'UNASSIGNED' } migrationStatus = $status verificationMethod = if ($acceptedRecord) { 'Controlled Plant 3D 2026 Preview/Apply/repeated Apply/UNDO runtime acceptance' } else { 'Legacy source audit, contract, deterministic Preview, controlled Apply, idempotency, UNDO and visual acceptance' } runtimeEvidence = if ($acceptedRecord) { $acceptedRecord.Evidence } else { 'NOT REPORTED' } knownDifferences = if ($acceptedRecord) { '2026 implementation uses local immutable package staging and managed entry assembly; legacy loading mechanics are not retained.' } else { 'NOT ASSESSED' } decision = if ($acceptedRecord) { $acceptedRecord.Decision } else { 'MIGRATION_REQUIRED' } } } $ledger = [ordered]@{ schemaVersion = 1 scope = 'Plant 3D P_Rec_Annot active command slice generated from audited non-archive command registrations. This is a ledger foundation, not a claim of complete ecosystem inventory.' source = 'migration/audits/p-rec-annot-v1.0.4/evidence/commands.csv' capabilities = @($capabilities) } $parent = Split-Path -Parent $OutputPath New-Item -ItemType Directory -Path $parent -Force | Out-Null $temporary = Join-Path $parent ((Split-Path -Leaf $OutputPath) + '.tmp') $json = $ledger | ConvertTo-Json -Depth 8 [IO.File]::WriteAllText($temporary, $json + [Environment]::NewLine, [Text.UTF8Encoding]::new($false)) Move-Item -LiteralPath $temporary -Destination $OutputPath -Force Write-Output "CAPABILITY_LEDGER_WRITTEN=$OutputPath" Write-Output "CAPABILITY_COUNT=$($capabilities.Count)"