Python Stencil Framework: Tests & Skills (scripts/stencil_gen/tests/, scripts/stencil_gen/stencil_gen/benchmarks/, .claude/skills/, ralph_wiggum.sh)
Maturity: mature · Audited: 2026-05-29 · See Capability Audit · Onboarding
This is the documentation + verification surface for the SymPy stencil-derivation pipeline (scripts/stencil_gen/). It bundles four things: (a) the pytest suite that proves the symbolic/codegen/PHS core is correct and that discovered-optimal stencil parameters stay stable; (b) the benchmarks/ sub-package holding the Brady & Livescu 2019 reference problems used to score stencil stability; (c) five Claude Code skills (.claude/skills/) that orient a developer in how to drive the pipeline, sweeps, tests, and group-velocity analysis; and (d) ralph_wiggum.sh, the Bash plan-executor loop that ran the automated development that produced most of scripts/stencil_gen. It exists so a new developer can run, extend, and trust the Python tooling. The Python side already has detailed prose docs under scripts/stencil_gen/docs/* and docs/handoff/*; this doc orients and cross-links rather than duplicating them.
| File | Role |
|---|---|
scripts/stencil_gen/tests/conftest.py |
Shared fixtures (e2_1_uniform, e2_2_uniform, e4u_pipeline, e6u_pipeline, e8u_pipeline, assert_taylor_accuracy), the run_pipeline(p, nu, s) helper, and the --run-slow option + slow-skip collection hook. |
scripts/stencil_gen/tests/test_phs.py |
Largest regression file. _load_known_values() / module-level _KNOWN read sweeps/known_values.json and assert stability/GV at discovered optima (E2_1, E4_1, footprint). |
scripts/stencil_gen/tests/test_brady2d_stability.py |
Second regression file. KNOWN_VALUES_PATH / _load_known_values() drive Brady-Livescu 2D layered-stability regressions; pytest.skip guards for absent sub-keys. |
scripts/stencil_gen/tests/test_benchmarks.py |
The only tests for the benchmarks/ sub-package (PDE-residual, exact-solution-at-t0, FAMILIES enumeration, run_calibration(max_layer=1)). |
scripts/stencil_gen/tests/test_optimizer.py |
save/load_known_values roundtrip + --update-known-values CLI (via tmp_path/monkeypatch) and the alpha_basin_survey unit + slow end-to-end tests. |
scripts/stencil_gen/tests/fixtures/*_e4u1_reference.py |
Reference RBF boundary coefficient arrays (gaussian / multiquadric / tension E4u_1) hard-pasted into the C++ t-*_E4u_1 tests; the Python copies guard cross-language consistency to 1e-12. |
scripts/stencil_gen/sweeps/known_values.json |
Single source of truth for discovered-optimal stencil parameters; written by sweeps --update-known-values, read by the regression tests. |
scripts/stencil_gen/stencil_gen/benchmarks/brady_livescu_2d.py |
Brady & Livescu 2019 §4.3 2D varying-coefficient advection reference. |
scripts/stencil_gen/stencil_gen/benchmarks/brady_livescu_4_2.py |
Brady & Livescu 2019 §4.2 reflecting-hyperbolic eigenvalue reference (purely imaginary continuous spectrum) — strictest stability discriminator. |
scripts/stencil_gen/stencil_gen/benchmarks/brady2d_calibration.py |
Enumerates all (scheme, kernel, params) FAMILIES and runs layered stability scoring; produces the calibration table. |
scripts/stencil_gen/stencil_gen/benchmarks/alpha_basin_survey.py |
Plan-43.9c multi-seed basin survey (research analog of Brady & Livescu Table 4). |
.claude/skills/{stencil-pipeline,stencil-sweeps,stencil-testing,group-velocity-analysis,ralph-wiggum}/SKILL.md |
Claude Code onboarding skills (auto-triggered by their description); each delegates detail to a scripts/stencil_gen/docs/*_reference.md. |
ralph_wiggum.sh |
Bash plan-executor: loops Claude Code through work/plan/review/commit passes against a plan file, enforcing the RALPH_STATUS protocol and a worktree-drift baseline guard. |
Running the suite (always from scripts/stencil_gen):
SYMPY_CACHE_SIZE=50000 uv run pytest tests/ -x -q # default: 1035 of 1174 run
SYMPY_CACHE_SIZE=50000 uv run pytest tests/ -x -q --run-slow # + the 139 @pytest.mark.slowconftest fixtures (tests/conftest.py) — module-scoped derivation caches, reuse rather than recompute:
e2_1_uniform,e2_2_uniform→derive_e2_uniform_boundary(nu=1|2)e4u_pipeline,e6u_pipeline,e8u_pipeline→run_pipeline(p=2|3|4)(returns(updated_rows, solution_dict, w_syms, result))assert_taylor_accuracy(session) → callable checking row-wise Taylor moment conditions on aB_umatrixrun_pipeline(p, nu=1, s=0)— module-level helper:derive_boundary+build_conservation_system+solve_conservation- Hooks:
pytest_addoption(--run-slow)+pytest_collection_modifyitems(skipsslow-keyword items unless--run-slow)
Regression machinery:
test_phs.py:_load_known_values() -> dict | Noneand module global_KNOWN(test classes only definedif _KNOWN is not None). Readssweeps/known_values.json.test_brady2d_stability.py:KNOWN_VALUES_PATH,_load_known_values()(same file).sweeps/known_values.jsontop-level keys:E2_1,E4_1,footprint,brady2d_calibration,brady2d_sweep. (Nested*_gvkeys live underE2_1/E4_1.)
benchmarks package (from stencil_gen.benchmarks import ...):
brady_livescu_2d:exact_solution(x,y,t),initial_condition(x,y),c_x(x,y),c_y(x,y),psi(x,y),make_coefficient_field(N),inflow_bc_x(y,t),inflow_bc_y(x,t), constantsL_DOMAIN = sqrt(2),PSI_OFFSET = 0.25.brady_livescu_4_2:initial_u(x),initial_v(x),exact_solution(x,t) -> (u,v),continuous_eigenvalues(k_max=20) -> np.ndarray, constantL_DOMAIN = 1.0.brady2d_calibration:FAMILIES: list[tuple[str,str,dict,str]],run_calibration(...),format_calibration_table(results) -> str,_report_to_dict(report),_E4_CLASSICAL_ALPHA.alpha_basin_survey:run_survey(...),format_survey_table(survey) -> str(wired intostencil_gen.brady2d_cli --alpha-basin-survey).
Skills (invoke via the Skill tool; each is a SKILL.md with YAML name/description + body): stencil-pipeline, stencil-sweeps, stencil-testing, group-velocity-analysis, ralph-wiggum.
ralph_wiggum.sh CLI:
ralph_wiggum.sh --plan FILE --mode {plan|work|full} --max-iterations N \
[--model M] [--log-dir DIR] [--allow-dirty]Env overrides: RALPH_WIGGUM_{PLAN,MODE,MAX_ITERATIONS,MODEL,LOG_DIR,ALLOW_DIRTY,CLAUDE_FLAGS}. Status protocol: each Claude pass must print RALPH_STATUS: committed|done|blocked and RALPH_SUMMARY: <one-line>. Default log dir is .git/ralph-wiggum/.
Test suite — two layers. The bulk is deterministic unit/property tests over the symbolic core: test_temo, test_interior, test_boundary, test_codegen, test_codegen_e4u, test_printer, test_eval_e2_1, test_phs verify derivation, Taylor accuracy, and C++ codegen. Layered on top is a file-driven regression contract: sweeps discover optimal parameters and write sweeps/known_values.json; the regression test classes in test_phs.py and test_brady2d_stability.py load that JSON and re-assert stability / GV error / objective values at the stored optima. The sweep → JSON → regression loop is the project's mechanism for pinning research results so they cannot silently regress.
Slow-test gating. pyproject.toml registers the slow marker; conftest.pytest_collection_modifyitems deselects every slow-keyword item unless --run-slow is passed. So a default run collects 1174 tests but runs ~1035 — a green default run is not full coverage.
Skip-when-absent regression contract. Some regression sub-keys (the secondary footprint/tension-penalty *_gv keys, and the top-level brady2d_optima key) are intentionally not yet populated in known_values.json. The corresponding tests pytest.skip() rather than fail — a documented "activate once sweeps populate the keys" contract, not a stub. The primary *_gv keys (tension_gv, gaussian_gv, multiquadric_gv under E2_1/E4_1) are present and their tests actively pass.
benchmarks → stability scoring. brady_livescu_4_2.py (§4.2 reflecting hyperbolic) and brady_livescu_2d.py (§4.3 varying-coefficient advection) encode the reference PDEs. brady2d_calibration.py enumerates FAMILIES and runs the layered scoring (L1–L8). The L8 layer drives the compiled C++ solver via stencil_gen/cpp_bridge.py (which shells build/src/app/shoccs with a generated Lua config). Production callers are stencil_gen/brady2d_stability.py and stencil_gen/brady2d_cli.py.
ralph_wiggum loop. Each iteration builds a prompt heredoc (one of plan / plan-review / work / review), invokes claude -p --dangerously-skip-permissions --no-session-persistence, then parses RALPH_STATUS/RALPH_SUMMARY from the output. It snapshots git status as a baseline and aborts (exit 2) on worktree drift, and dies if Claude claims committed but HEAD did not advance. --mode full interleaves plan-refinement and work passes.
Add a test. Create tests/test_<module>.py mirroring the source module name; use pytest.approx for floats; cache expensive SymPy derivations behind @pytest.fixture(scope="module") (copy the e4u_pipeline/e6u_pipeline pattern in conftest.py, or add a new shared fixture there). Mark anything doing >10 eigendecompositions or a TEMO/Groebner derivation @pytest.mark.slow. Always export SYMPY_CACHE_SIZE=50000.
Add a regression test. Run a sweep with --update-known-values to populate the relevant key in sweeps/known_values.json, then add a TestRegression* class in test_phs.py that reads _KNOWN[...] and asserts stability. Guard with pytest.skip if the sub-key may be absent (follow the existing skip-when-absent classes).
Add a benchmark. Drop a module under stencil_gen/benchmarks/, then add a test class to tests/test_benchmarks.py with a finite-difference PDE-residual check + an exact-solution-at-t0 check (copy the existing classes).
Add a skill. Create .claude/skills/<name>/SKILL.md with YAML frontmatter (name + description — the description drives auto-triggering) and a concise body; keep a one-line pointer to the detailed scripts/stencil_gen/docs/*_reference.md. Note the harness write-block below — committing a new skill currently requires a manual human-in-the-loop step.
Extend ralph_wiggum. Edit one of the four build_*_prompt heredocs (plan / plan-review / work / review) or add a mode in main()'s case statement.
SYMPY_CACHE_SIZE=50000is mandatory. The default 1000 causes severe slowdowns on the large TEMO/Groebner symbolic expressions.- Default
pytest tests/runs 1035 of 1174 collected — 139@pytest.mark.sloware silently deselected unless--run-slow. A green default run is partial coverage. The full default selection takes minutes, not seconds. - Non-determinism in the BO/optimizer tests.
test_bo.pyandtest_optimizer.pywrap stochastic BoTorch/pymoo; a-xrun can stop on a flaky acquisition-optimization failure that passes on isolated re-run. Do not treat a single-xstop intest_boas a real regression. - Some regression tests
pytest.skip()when a sub-key is missing fromknown_values.json. The secondary footprint/tension-penalty*_gvkeys and the top-levelbrady2d_optimakey are absent right now, so those guards silently skip — coverage looks green but those specific paths are untested until a sweep populates the keys. (The primary*_gvkeys are present and asserted.) - Three of five skills are UNTRACKED in git (
stencil-pipeline,stencil-testing,ralph-wiggum); onlystencil-sweepsandgroup-velocity-analysisare committed. Their companion reference docs (docs/ralph_wiggum_reference.md,scripts/stencil_gen/docs/pipeline_reference.md,scripts/stencil_gen/docs/testing_reference.md) are also untracked. A harness permission layer (seedocs/handoff/operating_conventions.md) blocks all writes to.claude/skills/**; the two committed skills were landed via a manual human-in-the-loop workaround. A clean checkout at an older commit may lack the untracked assets. stencil-testing/SKILL.mdtest-count table and "~400 tests/~7s" claim are STALE. Real numbers: printer 16 (not 10), codegen 41 (not 29), group_velocity 64 (not 52), phs 101 (not 82); the table lists only 10 of 21 test files. Trust livepytest --collect-only, not that table.- cpp_bridge L8 path depends on a runnable
build/src/app/shoccs. As of 2026-06-04 the C++ build is fixed and green and the binary builds & loads (was the Kokkos 5.0→5.1.1create_graphAPI break, fixed 2026-06-04). The slow smoke test still requires a runnable binary; it guards only on binary absence (if not SHOCCS_BINARY.exists(): pytest.skip), so under--run-slowit exercises the real solver. Default runs deselect it. ralph_wiggum.shis strict and container-only. It refuses a dirty worktree (exit 2) unless--allow-dirty, aborts on worktree drift between passes, and dies ifcommittedis claimed withoutHEADadvancing. It unsetsCLAUDECODE(so it can run inside an existing Claude session) and uses--dangerously-skip-permissions— intended for container use only.ralph_wiggum.sh:152still embeds range-v3-migration prompt text ("the specific range-v3 patterns to replace") inbuild_plan_prompt— leftover from the original migration, misleading for today's non-migration plans. Default plan is still the staledoc/hypre-semi-struct-plan.md.
Verdict: mature. Evidence: 1174 tests collect cleanly with no collection errors (1035 default / 139 slow). The deterministic core (test_temo, test_interior, test_boundary, test_codegen, test_codegen_e4u, test_printer, test_eval_e2_1, test_phs) runs fully green in ~13s; the only observed failure was a known stochastic BoTorch flake in test_bo that passes on isolated re-run. Every benchmark module has real production callers (brady2d_stability.py, brady2d_cli.py) and tests. All five skills' referenced functions/CLI/docs resolve on disk. ralph_wiggum.sh is git-tracked, executable, and actively maintained (commit 9f09ecf, 2026-04-27, fixed a kill-0 polling hang). The drift is in narrative documentation, not broken references — so "mature but documentation-drifting."
Verified flagged items in this subsystem:
- Three untracked skills + companion docs (
stencil-pipeline,stencil-testing,ralph-wiggum) — partial-in-the-narrow-sense / verdict: finish (commit them). Content is complete and every reference resolves; the only deficiency is they have never been committed (blocked by the.claude/skills/**harness write-layer). Remediation = commit via the documented manual workaround. See Cleanup Plan. stencil-testing/SKILL.mdstale test counts + "~400/~7s" — partial / verdict: finish. Run instructions and fixture docs are accurate; the quantitative table is provably wrong. Regenerate from livepytest --collect-only. See Cleanup Plan.ralph_wiggum.shrange-v3 prompt text + no test harness — partial / verdict: finish. Functional and in active use, but:152carries misleading range-v3 phrasing and the script has zero automated tests / no CMake wiring. Fix = generalize the prompt line (and optionally the stale default plan). See Cleanup Plan.*_gv/brady2d_optimakeys absent fromknown_values.json— verdict: keep (data backfill, not a code defect). Primary*_gvregression keys are present and their tests pass;brady2d_optimais absent but its producer (optimize.py --update-known-values), CLI wiring, and consumer test are all implemented and unit-tested with mocks. Skip-when-absent is a documented populate-later contract.alpha_basin_survey.pyis research-oriented — verdict: keep. Feature-complete: real CLI entry point (brady2d_cli --alpha-basin-survey), documented invocation, 5 passing monkeypatched unit tests + 1 slow end-to-end regression against the published Brady-Livescu α. "Slow + research" is its intended nature, not immaturity.test_cpp_bridge.py/ cpp_bridge L8 — verdict: keep. The test file (30 hermetic cases via a fake-shell binary) and thecpp_bridge.pymodule (4 production callers) are mature. The C++ build is fixed and green as of 2026-06-04 (was the Kokkos 5.1create_graphbreak), so the L8 smoke test now drives a runnable binary. See Cleanup Plan.
No dead (zero-caller, safe-to-delete) items in this subsystem.
This subsystem is largely the test surface: 21 test_*.py modules, 1174 tests collected. There is no ctest label here — these are Python tests run via uv run pytest, not the C++ Catch2 suite. Coverage highlights: deterministic symbolic/codegen core (green), file-driven regression of discovered optima (test_phs.py, test_brady2d_stability.py), benchmark-package sanity (test_benchmarks.py, 17 tests), optimizer/BO/pareto/sweep machinery (test_optimizer.py, test_bo.py, test_pareto.py, test_sweep_*), and the cpp_bridge contract (test_cpp_bridge.py, hermetic). Not covered: ralph_wiggum.sh has no automated tests (Bash); SKILL.md files have no validation harness (which is why the stale counts went unnoticed). Conditional/disabled: 139 @pytest.mark.slow (deselected without --run-slow); 1 xfail (test_e4_1_conservation_fails_without_zeros, documented infeasibility); 1 skipif (COBYQA/scipy≥1.14 in test_optimizer); runtime pytest.skip guards for absent known_values.json sub-keys; the cpp_bridge L8 smoke test (skips on binary absence; the C++ build is fixed and green as of 2026-06-04, so under --run-slow it drives the runnable binary).
- Detailed Python references (the deep docs this doc deliberately does not duplicate):
scripts/stencil_gen/docs/testing_reference.md(test organization, fixtures),scripts/stencil_gen/docs/pipeline_reference.md(derivation pipeline),scripts/stencil_gen/docs/sweeps_reference.md,scripts/stencil_gen/docs/group_velocity_reference.md,scripts/stencil_gen/docs/brady2d_stability_reference.md,scripts/stencil_gen/docs/optimization_reference.md,scripts/stencil_gen/docs/pareto_reference.md,scripts/stencil_gen/docs/mfbo_reference.md,scripts/stencil_gen/docs/bl42_reference.md. Note:pipeline_reference.mdandtesting_reference.mdare currently untracked (see Gotchas). - ralph_wiggum reference:
docs/ralph_wiggum_reference.md(untracked). - Handoff narrative:
docs/handoff/{MASTER.md, framework_architecture.md, operating_conventions.md, completed_plans.md, scientific_findings.md, known_limitations.md, next_steps.md}—operating_conventions.mddocuments the.claude/skills/**write-block;scientific_findings.mdrecords the stability/GV results the regression tests pin. - Sibling reference docs: the stencil-pipeline / matrices / operators / stencils references under
docs/reference/cover the C++-side and the SymPy derivation internals consumed here. - Project entry:
CLAUDE.md(root) for the canonical sweep/pytest invocations.