fix(py): restore 68 batch indicators silently degraded to streaming classes (84cu)#14
Merged
Merged
Conversation
…4cu)
The generated TA registry added in 0.7.0 derived native batch symbol names
with pascal_to_snake() (SuperTrend -> super_trend), but the export_*! macros
emit `pub fn [<$name:lower>]` (SuperTrend -> supertrend). Every multi-word
name missed; the 44 single-word ones (rsi, sma, atr) passed only because
pascal_to_snake("Rsi") == "rsi".
_resolve_ta_binding treated the miss as a fallback and returned
native_streaming, so qw.supertrend was a *class* while qw.rsi was a
function -- no error, no warning, different calling convention. 68 of 221
indicators were affected. 0.6 had no registry layer and bound native
symbols directly, which is why it was unaffected.
- generate_api_stubs.py: derive batch names with .lower(), matching the macro
- api_slug_aliases.json: fix 4 stale snake_cased names; null out 3 aliases
declaring batch exports that do not exist (fall through to polars); drop
sr_monitor's SrInteractionMonitor, never exported to Python
- __init__.py: raise ImportError when a declared native_batch symbol is
absent instead of silently substituting the streaming class. native_batch
of None still falls through to streaming/polars unchanged.
- test_api_surface.py: assert declared symbols resolve against the build.
The prior test only checked a name was present, never that it resolved,
so native_batch: "super_trend" passed cleanly.
qw.supertrend(period=10, multiplier=3.0, high=..., low=..., close=...) again
returns list[SuperTrendResult]; callers need no changes.
Verified on a real 0.7.0 venv: 221 slugs, 0 declared-batch-bound-as-class,
0 registry names absent from build. Suite 179 passed; the 2 failures
(test_sma_parity, test_correctness_precheck_runs) are pre-existing and
reproduce on a clean tree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
qw.supertrendwas a class.qw.rsiwas a function. Same registry, same generator, no error, no warning — and the difference was pure luck.The generated TA registry added in 0.7.0 (
5ipk.3) derives native batch symbol names withpascal_to_snake()(SuperTrend→super_trend), but theexport_*!macros emitpub fn [<$name:lower>](SuperTrend→supertrend). Every multi-word name missed. The 44 single-word names (rsi,sma,atr) survived only becausepascal_to_snake("Rsi") == "rsi"._resolve_ta_bindingtreated the miss as a fallback and returnednative_streaming— a class with a completely different calling convention. 68 of 221 indicators were affected. 0.6 had no registry layer and bound native symbols directly, which is why it was unaffected: this is a 0.7 regression.Why this matters beyond the bug
This is the
GMM::fit()disease fromplanning/BRUTAL_REVIEW_2026-07-07.md: a public API reporting success while doing something other than what the caller asked. The cost here was not just wrong behavior — the silence manufactured a false explanation. An agent investigatingqw.supertrendbeing a class concluded 0.7 had intentionally moved SuperTrend to the.taexpression namespace, and planned a downstream compiler refactor around a two-line codegen bug.The test could not catch it by construction:
It asserted a name was present, never that it resolved.
native_batch: "super_trend"passed cleanly.Changes
scripts/generate_api_stubs.py— derive batch names with.lower(), matching the macro. Repairs 61.scripts/api_slug_aliases.json— 4 stale snake_cased names (fm_demodulator,fourier_series_model,my_rsi,precision_trend_analysis); 3 aliases declaring batch exports that do not exist now fall through to their polars methods (linreg,oc2,true_range);sr_monitordroppedSrInteractionMonitor, a class that exists only as a string inmetadata_registry.rsand was never exported to Python.quantwave-py/python/quantwave/__init__.py—_resolve_ta_bindingraisesImportErrorwhen a declarednative_batchsymbol is absent from the build, instead of silently substituting the streaming class. Anative_batchofNonestill falls through to streaming/polars unchanged (intentionally streaming-only indicators keep working).tests/python/test_api_surface.py— assert every declared native symbol resolves against the compiled module, plus a regression test that multi-word slugs bind as batch functions. This test found a 69th mismatch (sr_monitor) during development._ta_registry_generated.py— regenerated: 68 insertions, 68 deletions, exactly the predicted blast radius.Compatibility
None required.
qw.supertrend(period=10, multiplier=3.0, high=..., low=..., close=...)again returnslist[SuperTrendResult]— the exact 0.6 calling convention.Verification
Run against a real 0.7.0 environment:
tests/python/: 179 passed. The 2 failures (test_sma_parity,test_correctness_precheck_runs) are pre-existing and reproduce on a clean tree viagit stash.Follow-up (not in this PR)
test_sma_parityfails ondf.lazy().ta.sma(...)with'LazyFrame' object has no attribute 'ta'. The.tanamespace is registered onExpr, not LazyFrame — the test encodes the same wrong mental model that misled the agent investigating this bug. Worth fixing separately.Closes
quantwave-84cu🤖 Generated with Claude Code