param( [Parameter(Mandatory = $true)][string]$Repository, [ValidatePattern('^[a-z0-9-]+$')][string]$RunName = 'verified' ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest $repoRoot = [IO.Path]::GetFullPath($Repository) $utf8 = [Text.UTF8Encoding]::new($false, $true) $exe = Join-Path $repoRoot '.local/source-cli-20260911/verified/arcadia.exe' $expectedExeHash = 'BAD169F8BE78DCFA74B32714569C886DD2132727080684FC2BA618D5822E3559' $runRoot = Join-Path $repoRoot ('.local/source-cli-independent-20260911/' + $RunName) if (Test-Path -LiteralPath $runRoot) { throw "Refusing to replace prior evidence: $runRoot" } $checks = [Collections.Generic.List[string]]::new() $operations = [Collections.Generic.List[object]]::new() function Assert-That([bool]$condition, [string]$label) { if (!$condition) { throw "Independent contract failed: $label" } $checks.Add($label) } function Put([string]$path, [string]$value) { [IO.Directory]::CreateDirectory([IO.Path]::GetDirectoryName($path)) | Out-Null [IO.File]::WriteAllText($path, $value, $utf8) } function Read-Exact([string]$path) { [IO.File]::ReadAllText($path, $utf8) } function Text-Hash([string]$text) { [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($utf8.GetBytes($text))) } function Invoke-ArcadiaProbe([string[]]$words) { $info = [Diagnostics.ProcessStartInfo]::new($exe) $info.UseShellExecute = $false $info.CreateNoWindow = $true $info.RedirectStandardOutput = $true $info.RedirectStandardError = $true $info.StandardOutputEncoding = $utf8 $info.StandardErrorEncoding = $utf8 foreach ($word in $words) { $info.ArgumentList.Add($word) } $info.ArgumentList.Add('--json') $process = [Diagnostics.Process]::new() $process.StartInfo = $info $process.Start() | Out-Null $output = $process.StandardOutput.ReadToEndAsync() $errorText = $process.StandardError.ReadToEndAsync() if (!$process.WaitForExit(60000)) { $process.Kill($true) throw 'CLI probe exceeded 60 seconds' } $raw = $output.GetAwaiter().GetResult() $err = $errorText.GetAwaiter().GetResult() $report = $raw | ConvertFrom-Json -Depth 100 Assert-That ($process.ExitCode -eq $report.exit_code) 'process exit agrees with JSON' Assert-That ($err -ceq '') 'JSON invocation emits no mixed stderr' $operations.Add([ordered]@{ Arguments = @($words | ForEach-Object { $_.Replace($runRoot, '') }) Exit = $process.ExitCode Status = $report.status Changes = @($report.changes).Count Diagnostics = @($report.diagnostics).Count StdoutSHA256 = Text-Hash $raw }) $process.Dispose() return $report } Assert-That ((Get-FileHash -LiteralPath $exe).Hash -ceq $expectedExeHash) 'immutable CLI binary pin' [IO.Directory]::CreateDirectory($runRoot) | Out-Null # Predict every changed header/reference before the first mutating invocation. # This fixture has explicit UUIDs for all seven Project::identities families. $sourcePath = Join-Path $repoRoot 'harness/experiments/textual-authoring-20260911/observed/stabilized.arcadia' $source = Read-Exact $sourcePath $sourceHash = Text-Hash $source $aliases = @( [pscustomobject]@{ Family = 'project'; Kind = 'project'; Old = 'alarm_response'; New = 'alarm_suite'; References = 0 }, [pscustomobject]@{ Family = 'element'; Kind = 'oa_entity'; Old = 'maintenance'; New = 'maintenance_staff'; References = 1 }, [pscustomobject]@{ Family = 'relation'; Kind = 'interaction'; Old = 'alarm_message'; New = 'alarm_payload'; References = 1 }, [pscustomobject]@{ Family = 'diagram'; Kind = 'diagram'; Old = 'response_view'; New = 'response_map'; References = 2 }, [pscustomobject]@{ Family = 'placement'; Kind = 'node'; Old = 'detector_card'; New = 'detector_tile'; References = 0 }, [pscustomobject]@{ Family = 'artifact'; Kind = 'artifact'; Old = 'response_procedure'; New = 'response_handbook'; References = 0 }, [pscustomobject]@{ Family = 'trace'; Kind = 'trace'; Old = 'assessment_refinement'; New = 'assessment_alignment'; References = 0 } ) $literal = ($aliases | ForEach-Object { '@' + $_.Old + ';' }) -join ' ' $source = $source.Replace('Numerical annotations are descriptive.', 'Numerical annotations are descriptive. Literal aliases: ' + $literal) $source += "`r`n// Retain these comment references: $literal`r`n" $expected = $source $predictions = [Collections.Generic.List[object]]::new() foreach ($case in $aliases) { $header = $case.Kind + ' ' + $case.Old + ' ' Assert-That (([regex]::Matches($expected, [regex]::Escape($header))).Count -eq 1) ($case.Family + ' has one literal declaration header') $next = $expected.Replace($header, $case.Kind + ' ' + $case.New + ' ') # The known live references occupy these exact fields; prose/comments are excluded. $field = switch ($case.Family) { 'element' { 'source' }; 'relation' { 'exchange' }; 'diagram' { 'diagram' }; default { $null } } if ($null -ne $field) { $oldReference = $field + ' @' + $case.Old + ';' Assert-That (([regex]::Matches($next, [regex]::Escape($oldReference))).Count -eq $case.References) ($case.Family + ' live-reference count') $next = $next.Replace($oldReference, $field + ' @' + $case.New + ';') } $predictions.Add([pscustomobject]@{ Case = $case; Before = $expected; After = $next }) $expected = $next } $aliasEntry = Join-Path $runRoot 'aliases/project.arcadia' Put $aliasEntry $source Put (Join-Path $PSScriptRoot 'alias-input.arcadia') $source Put (Join-Path $PSScriptRoot 'alias-expected-final.arcadia') $expected $baseline = Invoke-ArcadiaProbe @('check', $aliasEntry) Assert-That ($baseline.exit_code -eq 0) 'all-family baseline is admitted before mutation' Assert-That ($baseline.identities_to_stabilize -eq 0) 'all-family baseline has no missing identities' Assert-That (([regex]::Matches($source, ' id "[0-9a-f-]{36}"')).Count -eq 40) 'all-family baseline contains exactly 40 authored UUIDs' foreach ($prediction in $predictions) { $case = $prediction.Case $preview = Invoke-ArcadiaProbe @('rename-alias', $aliasEntry, $case.Old, $case.New) Assert-That ($preview.exit_code -eq 0 -and !$preview.written -and $preview.status -ceq 'preview') ($case.Family + ' preview succeeds without write') Assert-That ((Read-Exact $aliasEntry) -ceq $prediction.Before) ($case.Family + ' preview preserves exact input bytes') Assert-That (@($preview.changes).Count -eq 1) ($case.Family + ' reports exactly one changed file') Assert-That ($preview.changes[0].path -ceq 'project.arcadia') ($case.Family + ' preserves file ownership') Assert-That ($preview.changes[0].before -ceq $prediction.Before) ($case.Family + ' reports exact captured text') Assert-That ($preview.changes[0].after -ceq $prediction.After) ($case.Family + ' changes only independently predicted header/live-reference tokens') Assert-That ($preview.identities_to_stabilize -eq 0 -and $null -eq $preview.migration) ($case.Family + ' invents no identity or migration') $written = Invoke-ArcadiaProbe @('rename-alias', $aliasEntry, $case.Old, $case.New, '--write') Assert-That ($written.exit_code -eq 0 -and $written.written -and $written.status -ceq 'written') ($case.Family + ' write succeeds') Assert-That ($written.changes[0].after -ceq $prediction.After) ($case.Family + ' stable separate write invocation has exact preview text') Assert-That ((Read-Exact $aliasEntry) -ceq $prediction.After) ($case.Family + ' persisted text equals independent expectation') } $afterCheck = Invoke-ArcadiaProbe @('check', $aliasEntry) Assert-That ($afterCheck.exit_code -eq 0) 'all-family final source is freshly admitted' $mtime = [IO.File]::GetLastWriteTimeUtc($aliasEntry) $noop = Invoke-ArcadiaProbe @('stabilize', $aliasEntry, '--write') Assert-That ($noop.exit_code -eq 0 -and $noop.status -ceq 'unchanged' -and !$noop.written -and @($noop.changes).Count -eq 0) 'stable all-family source is a true no-op' Assert-That ([IO.File]::GetLastWriteTimeUtc($aliasEntry) -eq $mtime) 'no-op preserves source modification time' Assert-That ((Read-Exact $aliasEntry) -ceq $expected) 'no-op preserves every source byte' Assert-That (@(Get-ChildItem -LiteralPath (Split-Path -Parent $aliasEntry) -File -Recurse -Force).Count -eq 1) 'writes leave no unexpected physical file' # Two warnings in one CRLF/Unicode include expose the shifted-second-span trap. $rawEntry = Join-Path $runRoot 'raw/project.arcadia' $workPath = Join-Path $runRoot 'raw/work.arcadia' $entryText = 'arcadia 7; project diagnostic_case "Diagnostics" id "11000000-0000-4000-8000-000000000001" { include "work.arcadia"; }' + "`r`n" $rawWork = "// Пролог 😀`r`n" + 'sa_function first "航海" {}' + "`r`n" + 'sa_function second "Deuxième" {}' + "`r`n" Put $rawEntry $entryText Put $workPath $rawWork function Check-Warnings($report, [string]$rawText, [bool]$explicit) { Assert-That ($report.exit_code -eq 0) 'partial unallocated functions remain valid' $warnings = @($report.diagnostics | Where-Object { $_.code -ceq 'unallocated_leaf' }) Assert-That ($warnings.Count -eq 2) 'exactly two raw unallocated-leaf diagnostics without stabilized duplicates' $index = 0 foreach ($alias in @('first', 'second')) { $start = $utf8.GetByteCount($rawText.Substring(0, $rawText.IndexOf('sa_function ' + $alias, [StringComparison]::Ordinal))) $warning = @($warnings | Where-Object { $_.location.start -eq $start }) Assert-That ($warning.Count -eq 1) ($alias + ' warning starts at original UTF-8 declaration offset') Assert-That ($warning[0].coordinate_space -ceq 'input' -and $warning[0].location.path -ceq 'work.arcadia') ($alias + ' warning identifies raw include') Assert-That ($warning[0].location.line -eq ($index + 2) -and $warning[0].location.column -eq 1) ($alias + ' CRLF/Unicode line and scalar column') $bytes = $utf8.GetBytes($rawText) $location = $warning[0].location Assert-That ($location.end -gt $start -and $location.end -le $bytes.Length) ($alias + ' warning ends inside raw input') $slice = $utf8.GetString($bytes, $start, $location.end - $start) Assert-That ($slice.Contains('sa_function ' + $alias)) ($alias + ' raw diagnostic slice identifies intended declaration') $expectedId = if ($explicit) { '11000000-0000-4000-8000-00000000000' + ($index + 2) } else { $null } Assert-That ($warning[0].object_id -ceq $expectedId) ($alias + ' diagnostic identity has correct authored/provisional status') $index++ } } $rawCheck = Invoke-ArcadiaProbe @('check', $rawEntry) Check-Warnings $rawCheck $rawWork $false $rawPreview = Invoke-ArcadiaProbe @('stabilize', $rawEntry) Check-Warnings $rawPreview $rawWork $false Assert-That ((Read-Exact $rawEntry) -ceq $entryText -and (Read-Exact $workPath) -ceq $rawWork) 'raw diagnostics and preview retain both exact source files' Assert-That (@($rawPreview.changes).Count -eq 1 -and $rawPreview.changes[0].path -ceq 'work.arcadia') 'only missing-ID include is proposed for stabilization' $candidateText = $rawPreview.changes[0].after Assert-That ($utf8.GetByteCount($candidateText.Substring(0, $candidateText.IndexOf('sa_function second'))) -gt $utf8.GetByteCount($rawWork.Substring(0, $rawWork.IndexOf('sa_function second')))) 'fixture genuinely shifts second declaration in proposed candidate' $authoredWork = $rawWork.Replace('first "航海" {', 'first "航海" id "11000000-0000-4000-8000-000000000002" {').Replace('second "Deuxième" {', 'second "Deuxième" id "11000000-0000-4000-8000-000000000003" {') Put $workPath $authoredWork $authoredCheck = Invoke-ArcadiaProbe @('lint', $rawEntry) Check-Warnings $authoredCheck $authoredWork $true Assert-That ((Read-Exact $workPath) -ceq $authoredWork) 'authored-ID lint is read-only' # A non-BMP symbol before the bad reference distinguishes scalar columns from UTF-16 units. $invalidWork = "// Пролог 😀`r`n" + 'sa_function invalid "Deuxième 🚨" { parent @absent; }' + "`r`n" Put $workPath $invalidWork $invalid = Invoke-ArcadiaProbe @('stabilize', $rawEntry, '--write') Assert-That ($invalid.exit_code -eq 1 -and !$invalid.written -and @($invalid.changes).Count -eq 0) 'invalid raw Unicode source is refused before any candidate write' $referenceIndex = $invalidWork.IndexOf('@absent', [StringComparison]::Ordinal) $referenceStart = $utf8.GetByteCount($invalidWork.Substring(0, $referenceIndex)) $errorAt = @($invalid.diagnostics | Where-Object { $_.severity -ceq 'error' -and $_.location.start -eq $referenceStart }) Assert-That ($errorAt.Count -eq 1) 'unresolved reference uses exact raw UTF-8 byte start' $linePrefix = $invalidWork.Substring($invalidWork.IndexOf("`n") + 1, $referenceIndex - $invalidWork.IndexOf("`n") - 1) $scalarColumn = @($linePrefix.ToCharArray() | Where-Object { ![char]::IsLowSurrogate($_) }).Count + 1 Assert-That ($errorAt[0].location.line -eq 2 -and $errorAt[0].location.column -eq $scalarColumn) 'non-BMP prefix uses Unicode scalar column rather than UTF-16 units' Assert-That ($errorAt[0].coordinate_space -ceq 'input' -and $null -eq $errorAt[0].object_id) 'invalid input exposes neither candidate coordinate nor ephemeral identity' Assert-That ((Read-Exact $workPath) -ceq $invalidWork -and (Read-Exact $rawEntry) -ceq $entryText) 'invalid write retains complete input bundle bytes' Assert-That ((Get-FileHash -LiteralPath $exe).Hash -ceq $expectedExeHash) 'CLI pin remains unchanged after probes' $result = [ordered]@{ Reviewer = 'Codex-9e8r6t /root/behavior_audit' RecordedUtc = [DateTime]::UtcNow.ToString('O') BinarySHA256 = $expectedExeHash InitialPublishedFixtureSHA256 = $sourceHash AliasInputSHA256 = Text-Hash $source AliasExpectedFinalSHA256 = Text-Hash $expected CheckCount = $checks.Count Checks = $checks.ToArray() Operations = $operations.ToArray() Limits = 'Independent CLI process/source-byte checks only; no production compile, full Project comparison, concurrency proof, Linux or native GUI claim.' } Put (Join-Path $PSScriptRoot 'result.json') ($result | ConvertTo-Json -Depth 100) Write-Output "PASS: $($checks.Count) independent assertions across $($operations.Count) frozen CLI invocations."