Skip to main content

Scripts

The PowerShell and Python in both repositories, what each one actually does, and the language gotchas that cost time.

Inventory​

Source repository — C:\0DEV0\raybot​

ScriptLinesRole
coral-schema/extract-surface.ps131Recovers the JSON payload from a browser_evaluate tool-output temp file and writes surfaces/<Name>.json
coral-schema/build-validation.ps1110Aggregates the per-surface JSON into schema-validation.json and ui-elements-dump.json
coral-schema/build-html-report.ps1209Emits the single-file coral-ui-audit.html with all datasets inlined
coral-schema/rename-screenshots.ps162Maps 41 original captures to curated NN_<phase>_<subject>.png names with captions
coral-schema/annotate.py80Draws a title banner and labeled callout boxes on one screenshot
coral-schema/annotate_batch.py362Applies a per-image callout spec to the whole 2026-06-17 capture set

Wiki repository — C:\0DEV0\raybot-wiki​

ScriptRole
scripts/export-wiki-data.ps1Reads the audit artifacts from the source repository and emits the normalized JSON dataset in data/ that both the wiki and @raybot/object-model consume
scripts/open-issues.ps1Files the twelve tracking issues with their labels. Kept in-tree because the issue bodies are the page specifications

extract-surface.ps1: parsing tool output​

The DOM harvest ran as a browser_evaluate expression returning a JSON string. The tool wrote that return value to a temp file as a JSON string literal — the payload double-encoded — followed by echoed source text:

### Result
"{\"provenance\":{...},\"elements\":[...]}"
<echoed source>

Naive parsing fails twice over: the file is not valid JSON as a whole, and the first ConvertFrom-Json yields a string rather than an object. The script walks from the first quote to the matching unescaped closing quote, honoring backslash escapes, then converts twice — once to recover the inner JSON text, once to validate it — and writes the inner text verbatim.

The double conversion is the point. $inner is what gets written to disk; $obj exists only so that malformed payloads fail loudly at extraction time rather than silently at aggregation time.

build-validation.ps1: the aggregate​

This is where every headline number in the metrics page originates — surfaces, elements, interactive count, testid coverage, unique testids, label gaps, and shared testids.

Two details matter downstream:

Shared testids are truncated. The script publishes only the top 40:

sharedTestIdsAcrossSurfaces = ($sharedAcrossSurfaces |
Sort-Object surfaces -Descending | Select-Object -First 40)

The true count is 76. Any consumer that reads shared-testids.json as complete will undercount by 36, which is why this wiki's SharedTestIdTable derives from testids.json instead.

The $meta key is a literal. The aggregated dump uses '$meta' as a hashtable key. In the first HTML report build that key leaked into generated JavaScript as APIMAP['$meta'] in a context where it broke parsing. The fix is recorded in the Darbot validation pass as the "Prior APIMAP['$meta'] bug — Gone" row.

rename-screenshots.ps1: now a re-run guard​

Originally this script read originals from the repository root and wrote curated copies into screenshots/. After the 2026-06-17 consolidation moved all 66 root PNGs into screenshots/, it was rewritten so that both source and destination are screenshots/:

$dest = Join-Path $rb 'screenshots'
$src = Join-Path $dest $old
Copy-Item $src (Join-Path $dest $newName) -Force

It is now effectively idempotent — a no-op re-run guard. Its remaining value is as data: the [ordered] map is the authoritative filename-to-caption-to-phase crosswalk, and it is what gallery.json and this wiki's screenshot map are built from.

It also fails loudly on drift. Any original missing from screenshots/ produces Write-Warning "MISSING SOURCE: $old" and is skipped, so the reported copy count diverging from 41 is a signal that the capture set has been disturbed.

PowerShell gotchas that cost time​

$PSScriptRoot is empty inside a param() default​

Under Windows PowerShell 5.1, $PSScriptRoot is not populated at the time param() defaults are evaluated. This does not throw — it silently yields an empty string, and Join-Path on an empty first argument produces a path relative to the current directory. The script then writes its output somewhere plausible but wrong.

The fix is to default the parameter to $null and resolve the path in the body:

param(
[string]$Source = 'C:\0DEV0\raybot',
[string]$Dest
)

if (-not $Dest) {
$repoRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$Dest = Join-Path (Split-Path -Parent $repoRoot) 'data'
}

extract-surface.ps1 sidesteps the same problem by using $PSCommandPath, which is populated in the body.

[ordered]@{} does not expose keys as properties​

An ordered dictionary is an OrderedDictionary, not a PSCustomObject. Property-style access returns $null rather than throwing:

$h = [ordered]@{ surface = 'build' }
$h.surface # works - OrderedDictionary happens to support this
$h | Select-Object -ExpandProperty surface # fails - no such property

The failure mode is a pipeline that quietly produces nothing. Use index access, $h['surface'], or convert with [pscustomobject]$h before piping. build-validation.ps1 converts deliberately — [pscustomobject]@{ ... } for anything destined for Format-Table, [ordered]@{ ... } only for things destined for ConvertTo-Json where key order is the goal.

Writing JSON without a BOM​

Set-Content -Encoding UTF8 writes a BOM under Windows PowerShell 5.1. Tools that read the file as plain UTF-8 see \ufeff as the first character and fail to parse. export-wiki-data.ps1 uses the .NET writer explicitly:

[System.IO.File]::WriteAllText($Path, $json, (New-Object System.Text.UTF8Encoding($false)))

The $false argument is what suppresses the BOM. This is wrapped as Write-JsonFile and used for every emitted dataset so that re-running the export produces reviewable diffs rather than whole-file churn.

The Python annotators​

annotate.py takes a source image, a destination, a banner title, and a list of x,y,w,h,Label callout specs in source-image pixel coordinates. It draws a rounded accent rectangle per callout plus a label chip, and a title banner across the top.

annotate_batch.py embeds the same drawing engine and adds a per-image callout specification for the 2026-06-17 set, with coordinates verified against each raw screenshot at 1869x894 or 1869x895. A leading ! on a label switches that callout from the blue accent to crimson for emphasis.

Both depend on Pillow and on Segoe UI or Arial being resolvable by filename, with a fallback chain. The hardcoded resolution is the fragile part: re-capturing at a different viewport size invalidates every coordinate in the 362-line spec.

A forensic note​

build-validation.ps1 and build-html-report.ps1 both carry a hardcoded default path:

C:\Users\dayour\OneDrive - Microsoft\Documents\Clawpilot\raybot-runbook\coral-schema

That directory no longer exists on C:, D:, or E:. It is the strongest surviving evidence that the original Coral build ran under a different client from a different working directory, and it corroborates the legacy runbook's Step 0, which creates exactly that path. See provenance.