You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
chore: Migrate all scripts (js, ps1, bash, batch, etc) to typed python via uv #171
Every script-like thing in our repo should be typed python via uv. Reading js, ps1, etc is quite difficult and it doesn't make sense to have all of these different surfaces.
Where it makes sense to keep some things, like node/package/npm commands, this is ok, but ideally, as MUCH of the "non-unity" code in this repo moves to typed python + uv as possible.
First slice delivered in PR #172: the harness surface is now a typed Python project executed through uv.
What landed:
pyproject.toml + uv.lock at the repo root: zero runtime dependencies, requires-python >= 3.9, pyright strict configuration; scripts/gate.py passes strict pyright.
scripts/gate.py runs the whole non-Unity review ladder (CSharpier, Prettier, member-order/null-assertions/usings, assembly policy, skills index + drift, .llm instructions + file lengths, release credentials, line endings, the full harness self-test suite, npm pack dry run) as six parallel lanes. It only orchestrates the repository's existing checks; no check logic moved.
Per-step wall times on the console, --report <path> machine-readable JSON (schema 1: environment + per-step command/seconds/exit), --plan dry plan, --only <step> failed-step re-run with dependencies. Wired as npm run check:gate; self-tested in scripts/tests/test-gate.ps1 through --plan (interpreter-only, no toolchain needed); harness-scope.psm1 maps the new surface so CI keeps running the scoped selection.
The self-test suite runs as its own phase after the quick lanes: measured all-concurrent vs phased on this bind-mounted checkout, the suite saturating the machine made every other lane 3-4x slower.
CI untouched: no workflow changes, same scoped self-test selection, same checks. The local gate now also runs the release-credential and line-ending checks the old ladder only ran in CI, and drops the redundant post-format check steps locally (CI still runs them; a successful format already proves the same invariant).
Measured on this checkout (Linux container, 12 vCPU, bind-mounted working tree): the quick lanes run in ~8-16 s concurrently vs ~25-30 s serial; the harness self-test suite remains the long pole (37-105 s depending on host noise) and is unchanged by this PR. The gate's value so far is one-command ergonomics, the two added checks, and the measurement artifact rather than a large end-to-end wall reduction here.
Proposed migration order for the rest of this issue (one bounded slice per PR, each carrying its self-test port or mapping so coverage never drops):
Fast-check trio: fast-check.ps1 + harness-scope.psm1 + should-run-self-tests.ps1 -> one typed Python module pair; port test-fast-check.ps1 / test-harness-scope.ps1. This is the agent hot path and removes the pwsh startup tax from every iteration. CI adoption (uv setup on both runners + timing check) belongs to this slice because CI shares should-run-self-tests.ps1 and run-all.ps1.
Lint scripts: the six node lints (lint-csharp-member-order, lint-csharp-null-assertions, lint-csharp-usings, lint-changelog-length, lint-line-endings, lint-release-secrets) and the four PowerShell lints, one PR per script with its self-test ported.
Release pipeline: scripts/release/*.mjs (six scripts) and their five self-test files last — the most battle-tested surface, migrated only after the pattern has proven itself on 1-3.
Self-test runner: run-all.ps1 once its subjects are Python (a PowerShell runner for Python tests is the last thing worth keeping).
Constraints carried forward: pwsh stays required until slice 4 is done (CI and run-unity-validation.ps1 still need it), and node stays for npm lifecycle per this issue's carve-out. The Unity-side harness (run-unity-validation.ps1) is out of scope unless the owner wants it migrated too.
Second slice delivered in PR #173: the fast-check trio is now typed Python.
What landed:
scripts/fast_check.py, scripts/harness_scope.py, and scripts/should_run_self_tests.py (stdlib-only, pyright strict, launched through uv) replace fast-check.ps1, harness-scope.psm1, and should-run-self-tests.ps1. Verdicts and fail-safe selection semantics are identical, and the plan became a JSON contract (--plan, schema 1) like the gate's.
Scheduling is the change: the two per-call git scans run concurrently (each measured ~1.1 s on the bind-mounted checkout, sequential before), and the selected check groups run as a dependency DAG instead of sequential blocks; self-tests stay a second phase so the suite keeps full throughput.
CI (llm-lint.yml) resolves the self-test scope with the system Python interpreter (python3/python fallback) instead of two pwsh launches — same scoped selection, no uv bootstrap, coverage unchanged. CI uv adoption stays deferred: a setup step would cost more per job than it saves for a stdlib-only script.
The trio's self-tests (test-fast-check.ps1, test-harness-scope.ps1) now drive the Python entry points, every original case kept plus a mapped-subject plan case and a base-SHA contract pin; the scope map, context, and forbidden-pattern references moved to the new names.
Measured (same-window, interleaved, N=7): plan floor on the bind-mounted checkout 2.8 s → 2.1 s via uv; whole-iteration A/B in a fresh local clone: scripts-only change 3.9 s → 2.9 s (-26%), scripts + C# + packaging change 5.6 s → 3.0 s (-47%).
One defect found during validation: importing the scope module wrote scripts/__pycache__/*.pyc, whose untracked surface paths fail-safed the next scan into the full suite — a self-inflicted full-suite loop that would also have broken CI's scoped skip. The entry scripts now suppress bytecode writes and .gitignore ignores the directory.
Remaining for this issue: the suite runner (run-all.ps1), the .js lint scripts, the release pipeline .mjs files, the remaining .ps1 helpers, and the docs build.
Third slice delivered in PR #174: the .llm lint trio and the self-test runner are typed Python.
What landed:
scripts/llm_config.py + scripts/generate_skills_index.py + scripts/lint_llm_instructions.py + scripts/lint_file_lengths.py (stdlib-only, pyright strict) replace LlmConfig.psm1, generate-skills-index.ps1, lint-llm-instructions.ps1, and lint-file-lengths.ps1. Verdicts, exit codes, message shapes, and flags are identical; the generated index is byte-identical apart from its banner, which now names the Python generator.
scripts/tests/run_all.py replaces run-all.ps1 as the suite runner: same discovered test-*.ps1 set, same concurrent child-pwsh execution with output replayed in name order, same --names fail-safe selection, same summary lines. The parent pwsh is gone from the hot path; the test files themselves remain pwsh (the remaining surface for this issue).
The gate and the fast check now derive their Python launcher from the host: uv run where uv is installed, the running interpreter where it is not. The first CI run caught this: the plan hardcoded uv, and test-gate.ps1's resolve-every-program check failed on uv-less runners — the plan now only describes commands that execute on the host that built it.
CI lint job: one "Resolve the Python interpreter" step validates the interpreter actually executes (the Windows python3 App-Execution-Alias stub answers command -v but is not a working interpreter; a garbage scope verdict would silently run the full suite on every Windows job), the three lint steps run through it, and the checkout is shallow since the scope step already fetches a missing base.
The trio's and the runner's self-tests drive the Python entries; every original case kept.
Measured (same-window interleaved, both sides on the same filesystem): harness lane per-step 0.82-1.33 s (pwsh) → 0.12-0.23 s (Python), lane total 3.3-3.6 s → 0.5-0.6 s (-80-85%); the self-test suite itself is unchanged (44.8/38.0 vs 45.1/49.1 s, load variance). A parallelism-cap experiment (throttle 4) lost to interleaved A/B (uncapped 31-41 s vs capped 51-56 s on 12 vCPU, parity on 4 pinned CPUs) and was reverted before landing — the spawn-bound suite really does want every child started immediately.
One more defect caught by CI:test-opencode-service-recycle lost its fake service once under the 28-way concurrent suite (ubuntu job) — recorded as #175 with log evidence; 25/25 serial green locally the same day, and the rerun on this PR passed.
Remaining for this issue: the six node lints, the release .mjs pipeline, build-docs.ps1, and the assembly/Unity-validation pwsh lints.
Fourth slice delivered in PR #176: the assembly warning-policy lint is typed Python, which retires the last PowerShell lint from the agent path.
What landed:
scripts/lint_assembly_warnings.py (stdlib-only, pyright strict) replaces scripts/lint-assembly-warnings.ps1. Verdicts, exit codes, message shapes, and the root-flag behavior are identical, including the case-sensitivity split the original encoded: response-file name checks stay case-sensitive (-ceq), every -match pattern stays case-insensitive, and hidden entries stay excluded from the recursive scan the way Get-ChildItem -Recurse excluded them.
test-assembly-warnings.ps1 now drives the Python entry through Find-Python; all 25 original cases kept, one file still, same assertions.
Callers repointed: the gate's assembly lane, the fast check's assembly_config group, the pre-commit hook, npm run lint:assembly-warnings, and the lint CI step (which now runs through the resolved ${PYTHON}). The retired path keeps its harness-scope entry so a deletion still selects its tests.
One process-behavior nuance preserved deliberately: the scan walks the same tree the PowerShell version walked (including node_modules), so verdict parity beats scan shortcuts.
Measured (same-window interleaved, min of 5 on this checkout): the lane command itself 4.25 s (pwsh) → 1.00 s (Python), 4.3x; the gate's assembly lane 7.4 s → 2.4 s across full gate runs, so markdown formatting is now the quick-phase long pole. On CI, the ubuntu lint job's pwsh step measured 4 s (run 38160432095) against roughly 1 s for every node/Python lint step; the Windows job measured 1 s. Lint CI needs PowerShell for no lint step after this; the self-test files remain pwsh, which is the remaining surface for this issue.
Remaining for this issue: the six node lints, the release-pipeline .mjs files, the docs build, run-unity-validation.ps1, and the pwsh self-test files themselves.
Fifth slice + a local-iteration finding (2026-10-11)
Slice (PR #177, harness robustness): the recycle contract fixture was the last fixture whose death degraded into wrong verdicts; it now proves spawn readiness with an exec marker, retries once, and fails by name (issue #175, which also red-lighted main at run 38163069900). Not a language port, but it removes the last suite flake that cost CI reruns.
Finding: the devcontainer checkout mount dominates local iteration time./workspaces here is a 9p drvfs mount of the Windows host drive (msize=64 KB); 9p metadata traffic dominates spawn-heavy harness work. Same-window A/B, identical tree and dirty surface, container-native ext4 clone vs the mount:
Measurement
9p (/workspaces)
ext4 (~/ clone)
fast-check plan floor
4.4 s
0.28 s
whole dirty-tree check:fast (same diff, both green)
25.6 s
12.1 s
full 28-file self-test suite
86.0 s
39.7 s
The suite number explains why hosted CI (native disk, 4 vCPU) measured 41 s while this 12-vCPU container measured 86 s. Recorded as agent guidance in PR #178 (clone under the Linux home for iteration loops; keep /workspaces for Unity host-bridge work). CI adoption of uv stays deferred as before; this finding is local-only by construction.
Remaining surface for this issue (unchanged): the six node lints, the release .mjs pipeline and its five self-test files, build-docs.ps1, run-unity-validation.ps1, and the 27 remaining pwsh self-test files. The suite is contention-bound (per-file serial walls 1.9-6.3 s vs 13-77 s under 28-way concurrency on 9p), so porting the self-test files to Python is the next structural lever for both local and CI-suite walls.
Activity
wallstop commentedon Oct 11, 2026
DISCLOSURE: LLM-GENERATED TEXT
First slice delivered in PR #172: the harness surface is now a typed Python project executed through uv.
What landed:
pyproject.toml+uv.lockat the repo root: zero runtime dependencies,requires-python >= 3.9, pyrightstrictconfiguration;scripts/gate.pypasses strict pyright.scripts/gate.pyruns the whole non-Unity review ladder (CSharpier, Prettier, member-order/null-assertions/usings, assembly policy, skills index + drift, .llm instructions + file lengths, release credentials, line endings, the full harness self-test suite, npm pack dry run) as six parallel lanes. It only orchestrates the repository's existing checks; no check logic moved.--report <path>machine-readable JSON (schema 1: environment + per-step command/seconds/exit),--plandry plan,--only <step>failed-step re-run with dependencies. Wired asnpm run check:gate; self-tested inscripts/tests/test-gate.ps1through--plan(interpreter-only, no toolchain needed);harness-scope.psm1maps the new surface so CI keeps running the scoped selection.checksteps locally (CI still runs them; a successful format already proves the same invariant).Measured on this checkout (Linux container, 12 vCPU, bind-mounted working tree): the quick lanes run in ~8-16 s concurrently vs ~25-30 s serial; the harness self-test suite remains the long pole (37-105 s depending on host noise) and is unchanged by this PR. The gate's value so far is one-command ergonomics, the two added checks, and the measurement artifact rather than a large end-to-end wall reduction here.
Proposed migration order for the rest of this issue (one bounded slice per PR, each carrying its self-test port or mapping so coverage never drops):
fast-check.ps1+harness-scope.psm1+should-run-self-tests.ps1-> one typed Python module pair; porttest-fast-check.ps1/test-harness-scope.ps1. This is the agent hot path and removes the pwsh startup tax from every iteration. CI adoption (uv setup on both runners + timing check) belongs to this slice because CI sharesshould-run-self-tests.ps1andrun-all.ps1.build-docs.ps1hand-rollspython -m venv .docs-venv+requirements-docs.txt(mkdocs, mkdocs-material); uv manages that environment natively.lint-csharp-member-order,lint-csharp-null-assertions,lint-csharp-usings,lint-changelog-length,lint-line-endings,lint-release-secrets) and the four PowerShell lints, one PR per script with its self-test ported.scripts/release/*.mjs(six scripts) and their five self-test files last — the most battle-tested surface, migrated only after the pattern has proven itself on 1-3.run-all.ps1once its subjects are Python (a PowerShell runner for Python tests is the last thing worth keeping).Constraints carried forward: pwsh stays required until slice 4 is done (CI and
run-unity-validation.ps1still need it), and node stays for npm lifecycle per this issue's carve-out. The Unity-side harness (run-unity-validation.ps1) is out of scope unless the owner wants it migrated too.Run the review gate as one fast command (#172)
wallstop commentedon Oct 11, 2026
DISCLOSURE: LLM-GENERATED TEXT
Second slice delivered in PR #173: the fast-check trio is now typed Python.
What landed:
scripts/fast_check.py,scripts/harness_scope.py, andscripts/should_run_self_tests.py(stdlib-only, pyright strict, launched through uv) replacefast-check.ps1,harness-scope.psm1, andshould-run-self-tests.ps1. Verdicts and fail-safe selection semantics are identical, and the plan became a JSON contract (--plan, schema 1) like the gate's.llm-lint.yml) resolves the self-test scope with the system Python interpreter (python3/pythonfallback) instead of two pwsh launches — same scoped selection, no uv bootstrap, coverage unchanged. CI uv adoption stays deferred: a setup step would cost more per job than it saves for a stdlib-only script.test-fast-check.ps1,test-harness-scope.ps1) now drive the Python entry points, every original case kept plus a mapped-subject plan case and a base-SHA contract pin; the scope map, context, and forbidden-pattern references moved to the new names.Measured (same-window, interleaved, N=7): plan floor on the bind-mounted checkout 2.8 s → 2.1 s via uv; whole-iteration A/B in a fresh local clone: scripts-only change 3.9 s → 2.9 s (-26%), scripts + C# + packaging change 5.6 s → 3.0 s (-47%).
One defect found during validation: importing the scope module wrote
scripts/__pycache__/*.pyc, whose untracked surface paths fail-safed the next scan into the full suite — a self-inflicted full-suite loop that would also have broken CI's scoped skip. The entry scripts now suppress bytecode writes and.gitignoreignores the directory.Remaining for this issue: the suite runner (
run-all.ps1), the.jslint scripts, the release pipeline.mjsfiles, the remaining.ps1helpers, and the docs build.Port the fast-check trio to typed Python
Check changes faster with a typed Python loop (#173)
Run the self-test suite through the typed Python runner
wallstop commentedon Oct 11, 2026
DISCLOSURE: LLM-GENERATED TEXT
Third slice delivered in PR #174: the .llm lint trio and the self-test runner are typed Python.
What landed:
scripts/llm_config.py+scripts/generate_skills_index.py+scripts/lint_llm_instructions.py+scripts/lint_file_lengths.py(stdlib-only, pyright strict) replaceLlmConfig.psm1,generate-skills-index.ps1,lint-llm-instructions.ps1, andlint-file-lengths.ps1. Verdicts, exit codes, message shapes, and flags are identical; the generated index is byte-identical apart from its banner, which now names the Python generator.scripts/tests/run_all.pyreplacesrun-all.ps1as the suite runner: same discoveredtest-*.ps1set, same concurrent child-pwsh execution with output replayed in name order, same--namesfail-safe selection, same summary lines. The parent pwsh is gone from the hot path; the test files themselves remain pwsh (the remaining surface for this issue).uv runwhere uv is installed, the running interpreter where it is not. The first CI run caught this: the plan hardcodeduv, andtest-gate.ps1's resolve-every-program check failed on uv-less runners — the plan now only describes commands that execute on the host that built it.python3App-Execution-Alias stub answerscommand -vbut is not a working interpreter; a garbage scope verdict would silently run the full suite on every Windows job), the three lint steps run through it, and the checkout is shallow since the scope step already fetches a missing base.Measured (same-window interleaved, both sides on the same filesystem): harness lane per-step 0.82-1.33 s (pwsh) → 0.12-0.23 s (Python), lane total 3.3-3.6 s → 0.5-0.6 s (-80-85%); the self-test suite itself is unchanged (44.8/38.0 vs 45.1/49.1 s, load variance). A parallelism-cap experiment (throttle 4) lost to interleaved A/B (uncapped 31-41 s vs capped 51-56 s on 12 vCPU, parity on 4 pinned CPUs) and was reverted before landing — the spawn-bound suite really does want every child started immediately.
One more defect caught by CI:
test-opencode-service-recyclelost its fake service once under the 28-way concurrent suite (ubuntu job) — recorded as #175 with log evidence; 25/25 serial green locally the same day, and the rerun on this PR passed.Remaining for this issue: the six node lints, the release
.mjspipeline,build-docs.ps1, and the assembly/Unity-validation pwsh lints.Port the .llm lints to typed Python (#174)
Record the probe-mutates-checkout trap in forbidden patterns
wallstop commentedon Oct 11, 2026
DISCLOSURE: LLM-GENERATED TEXT
Fourth slice delivered in PR #176: the assembly warning-policy lint is typed Python, which retires the last PowerShell lint from the agent path.
What landed:
scripts/lint_assembly_warnings.py(stdlib-only, pyright strict) replacesscripts/lint-assembly-warnings.ps1. Verdicts, exit codes, message shapes, and the root-flag behavior are identical, including the case-sensitivity split the original encoded: response-file name checks stay case-sensitive (-ceq), every-matchpattern stays case-insensitive, and hidden entries stay excluded from the recursive scan the wayGet-ChildItem -Recurseexcluded them.test-assembly-warnings.ps1now drives the Python entry throughFind-Python; all 25 original cases kept, one file still, same assertions.assembly_configgroup, the pre-commit hook,npm run lint:assembly-warnings, and the lint CI step (which now runs through the resolved${PYTHON}). The retired path keeps its harness-scope entry so a deletion still selects its tests.node_modules), so verdict parity beats scan shortcuts.Measured (same-window interleaved, min of 5 on this checkout): the lane command itself 4.25 s (pwsh) → 1.00 s (Python), 4.3x; the gate's assembly lane 7.4 s → 2.4 s across full gate runs, so markdown formatting is now the quick-phase long pole. On CI, the ubuntu lint job's pwsh step measured 4 s (run 38160432095) against roughly 1 s for every node/Python lint step; the Windows job measured 1 s. Lint CI needs PowerShell for no lint step after this; the self-test files remain pwsh, which is the remaining surface for this issue.
Remaining for this issue: the six node lints, the release-pipeline
.mjsfiles, the docs build,run-unity-validation.ps1, and the pwsh self-test files themselves.Pin the assembly lint CI step to bash
Port the assembly-warnings lint to Python (#176)
wallstop commentedon Oct 11, 2026
DISCLOSURE: LLM-GENERATED TEXT
Fifth slice + a local-iteration finding (2026-10-11)
Slice (PR #177, harness robustness): the recycle contract fixture was the last fixture whose death degraded into wrong verdicts; it now proves spawn readiness with an exec marker, retries once, and fails by name (issue #175, which also red-lighted main at run 38163069900). Not a language port, but it removes the last suite flake that cost CI reruns.
Finding: the devcontainer checkout mount dominates local iteration time.
/workspaceshere is a 9p drvfs mount of the Windows host drive (msize=64 KB); 9p metadata traffic dominates spawn-heavy harness work. Same-window A/B, identical tree and dirty surface, container-native ext4 clone vs the mount:The suite number explains why hosted CI (native disk, 4 vCPU) measured 41 s while this 12-vCPU container measured 86 s. Recorded as agent guidance in PR #178 (clone under the Linux home for iteration loops; keep /workspaces for Unity host-bridge work). CI adoption of uv stays deferred as before; this finding is local-only by construction.
Remaining surface for this issue (unchanged): the six node lints, the release .mjs pipeline and its five self-test files, build-docs.ps1, run-unity-validation.ps1, and the 27 remaining pwsh self-test files. The suite is contention-bound (per-file serial walls 1.9-6.3 s vs 13-77 s under 28-way concurrency on 9p), so porting the self-test files to Python is the next structural lever for both local and CI-suite walls.
Document native-FS iteration for WSL2 setups (#178)
Test the typed Python harness with typed Python
113 remaining items
Restore Unity connections across managed frontends (#241)
Isolate Windows tests from shell and newline defaults
Bound harness file workers during Windows CI
Run lint CI through uv and typed Python
Isolate Windows tests from shell and newline defaults
Bound harness file workers during Windows CI
Keep credential test probes independent of idle connections
Run lint CI through typed Python and uv (#247)
Run stdlib CI scripts without a project virtual environment
Isolate bridge startup instead of limiting every test file
Reuse the active Python interpreter for gate checks
Measure full validation alternately on the same CI runner
Bound bridge recovery waits and control comparison placement
Remove temporary CI comparison after passing both OS samples
Reuse Python for local gate checks (#249)