Skip to content

fix(state_null): resolve external hypothesis variables via enquo() env, not caller_env() - #22

Merged
joshuamarie merged 2 commits into
s7-stats:masterfrom
AntoineSoetewey:review/beyond-null-vignette
Aug 7, 2026
Merged

fix(state_null): resolve external hypothesis variables via enquo() env, not caller_env()#22
joshuamarie merged 2 commits into
s7-stats:masterfrom
AntoineSoetewey:review/beyond-null-vignette

Conversation

@AntoineSoetewey

@AntoineSoetewey AntoineSoetewey commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

@joshuamarie asked the team to review the beyond-null vignette (https://s7-stats.github.io/statim/articles/usage/beyond-null.html). The prose/methods checked out against the actual code and re-computed numbers, but rendering the vignette for real surfaced that Showcase 3 doesn't build at all: state_null() fails to resolve an external variable (overall_avg) referenced in the hypothesis expression.

Root cause: R/hypothesis-core.R re-derives the hypothesis expression's environment via rlang::caller_env() instead of using the environment rlang::enquo() already captured correctly. caller_env() counts call-stack frames, and the S7 dispatch machinery for state_null() inserts a variable number of frames depending on context — it happens to resolve correctly when called at the top level of an interactive script, but lands on an internal S7 dispatch environment (confirmed via ls(envir = env)) when called from inside a function, local(), or a knitr chunk. Since every vignette renders through knitr, this broke the documented "data-driven threshold" use case in practice, not just as an edge case.

Filed as #21 with full repro/root-cause details before opening this PR.

  • Swap caller_env() for quo_get_env(raw) in state_null() (R/hypothesis-core.R).
  • Align the vignette's \VignetteIndexEntry with its YAML title (was raising a title-mismatch warning on every render).
  • NEWS.md entry under Bug Fixes.

No vignette prose/numbers needed changing — everything cross-checked against re-computed output was already accurate.

Test plan

  • devtools::test() — full suite passes unchanged with the fix
  • rmarkdown::render("vignettes/usage/beyond-null.Rmd") — now completes with no errors and no title-mismatch warning
  • Reproduced the original failure and confirmed the fix inside a plain function and inside local(), not just in the vignette

Closes #21.

…v, not caller_env()

Reviewing the beyond-null vignette (requested by Joshua) surfaced that
Showcase 3 (a hypothesis referencing an external variable, `<= 0.20 *
overall_avg`) fails to render: `rlang::caller_env()` inside the S7
method for state_null() depends on the exact call-stack shape and does
not reliably land on the user's calling scope once state_null() is
invoked from inside a function, local(), or a knitr chunk - which is
exactly how every vignette is built. It only "worked" by accident when
called at the top level of an interactive script, which is presumably
how the numbers currently in the vignette were produced.

Fix: reuse the environment `enquo()` already captures correctly for
the expression, instead of re-deriving it via a stack-based lookup.
Full test suite passes unchanged after the fix.

Also aligned the vignette's \VignetteIndexEntry with its YAML title
(was raising a title-mismatch warning on render).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes state_null() failing to resolve externally defined variables referenced in hypothesis expressions (notably when invoked through S7 dispatch from within functions/knitr), which was breaking the beyond-null vignette render.

Changes:

  • Switch state_null() to use the environment captured by rlang::enquo() (rlang::quo_get_env(raw)) instead of rlang::caller_env().
  • Align beyond-null vignette \VignetteIndexEntry with its YAML title to remove render warnings.
  • Document the bug fix in NEWS.md.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
R/hypothesis-core.R Fixes hypothesis expression environment resolution for state_null() by using the captured quosure env.
vignettes/usage/beyond-null.Rmd Updates vignette index entry to match the title, avoiding title-mismatch warnings.
NEWS.md Adds a Bug Fixes entry describing the state_null() environment resolution fix and its impact.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread R/hypothesis-core.R
Comment on lines 26 to 30
S7::method(state_null, test_lazy) = function(.x, expr, ...) {
raw = rlang::enquo(expr)
expr_val = rlang::quo_get_expr(raw)
env = rlang::caller_env()
env = rlang::quo_get_env(raw)
claim = parse_null_claim(rlang::new_quosure(expr_val, env))
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Clarified the purpose of the vignette regarding weighted, non-zero-threshold claims and improved explanations of `state_null()` and `x_by()` functionality.
@joshuamarie
joshuamarie merged commit 0760a9b into s7-stats:master Aug 7, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

state_null() fails to resolve external variables outside top-level scripts (breaks beyond-null vignette)

3 participants