Skip to content

Make the locale test stub strict about unexpected variable reads (#489) - #502

Merged
leynos merged 8 commits into
mainfrom
issue-489-make-locale-stub-strict
Aug 6, 2026
Merged

Make the locale test stub strict about unexpected variable reads (#489)#502
leynos merged 8 commits into
mainfrom
issue-489-make-locale-stub-strict

Conversation

@leynos

@leynos leynos commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

test_support::locale_stubs::StubEnv answered None for every key but one:

impl EnvProvider for StubEnv {
    fn var(&self, key: &str) -> Option<String> {
        if key == locale_resolution::NETSUKE_LOCALE_ENV {
            return self.locale.clone();
        }
        None
    }
}

Had the code under test been changed to read a differently-named variable — a rename, a typo, or a new precedence rung — the stub would have quietly answered None and the test would still have passed, asserting nothing whatever about the new read. That is precisely the failure mode a test double exists to prevent.

The stub now declares which variables it answers and panics on anything else, naming the unexpected key.

Design points

Unset is a declarable state. without_locale() permits the read and reports it unset, distinct from a variable the test never anticipated. An absent variable is a legitimate case to exercise, and conflating the two would make the stub unusable for it.

Default is removed, not retained. On a strict stub it would mean "deny every read", so StubEnv::default() would compile and then panic at run time for the common "no locale set" case. Requiring without_locale() turns that into a compile error. One call site in tests/locale_resolution_tests.rs relied on it — and did indeed fail at run time before this change, which is how the trap was found.

Why not mockable::Env

This issue originally proposed converging locale_resolution::EnvProvider onto mockable::Env. On attempting it, that is the wrong trade.

mockable sits in [dev-dependencies] and reaches the crate only through test_support. locale_resolution is production code with a public API, so re-exporting mockable::Env from it requires moving mockable into [dependencies] — and cargo tree shows it pulling mockall:

mockable v3.0.0
├── mockall v0.11.4
└── tracing v0.1.44

Shipping a mocking framework in release builds of a build tool, to delete a two-method trait, is not a good exchange. The bespoke EnvProvider exists precisely so that does not happen — a sound reason, not the duplication the issue characterised it as. The AGENTS.md mandate already permits this shape, so locale_resolution was never in violation.

The one thing MockEnv's expectations would genuinely have bought is strictness about unexpected reads. This PR takes that without the dependency.

Verification

All gates pass: check-fmt, lint, typecheck, test (1188 nextest), markdownlint, nixie. CodeScene delta: no issues.

Closes #489.
Refs #496.

🤖 Generated with Claude Code

Summary by Sourcery

Tighten the locale environment test stub so tests explicitly declare expected environment variable reads and fail on unexpected ones.

New Features:

  • Allow tests to configure stubbed environment variables as either set or explicitly unset via StubEnv::with_var, StubEnv::with_locale, StubEnv::without_locale, and StubEnv::allowing.

Enhancements:

  • Make StubEnv a strict stub that panics when code under test reads undeclared environment variables, improving test fidelity and catching unintended variable usage.
  • Remove the Default implementation from StubEnv to prevent implicit, runtime-failing configurations and require explicit setup of the stub state.

Tests:

  • Update CLI and locale resolution BDD steps and unit tests to use the new strict StubEnv API and to explicitly declare NETSUKE_JSON as a legitimately readable-but-unset variable.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 237a20f6-3f50-4c81-bd9b-3cccded664f9

📥 Commits

Reviewing files that changed from the base of the PR and between 8ac4103 and 6fa22ce.

📒 Files selected for processing (10)
  • docs/developers-guide.md
  • test_support/src/locale_stubs.rs
  • tests/bdd/steps/cli.rs
  • tests/bdd/steps/locale_resolution.rs
  • tests/locale_resolution_tests.rs
  • tests/locale_stub_strictness_tests.proptest-regressions
  • tests/locale_stub_strictness_tests.rs
  • tests/locale_stub_ui_tests.rs
  • tests/ui/stub_env_default_compile_fail.rs
  • tests/ui/stub_env_strict_compile_pass.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/shared-actions (auto-detected)

Summary

  • Enforce explicit environment-variable declarations in StubEnv.
  • Panic on undeclared reads and report the variable name.
  • Support explicitly unset variables through without_locale().
  • Use strict(), with_locale, without_locale, with_var, and allowing for construction.
  • Remove StubEnv::Default.
  • Add property-based tests for declaration precedence and undeclared reads.
  • Add compile-fail coverage for StubEnv::default().
  • Document the strict-stub contract and its test coverage.
  • Address issue #489 without replacing locale_resolution::EnvProvider with mockable::Env.
  • Align the test support changes with the completed locale-resolution execplan: docs/execplans/3-7-2-locale-resolution.md.

Walkthrough

Use explicit StubEnv builders for configured and unset locales. Store declared values and allowed keys privately. Panic on undeclared reads. Validate declaration precedence with property-based tests and reject StubEnv::default() at compile time.

Changes

Locale stub strictness

Layer / File(s) Summary
Define strict StubEnv behaviour
test_support/src/locale_stubs.rs
Replace the public locale field with private value and allowlist storage. Add explicit constructors and builders. Panic on undeclared reads and apply last-declaration precedence.
Update locale test integrations
tests/bdd/steps/cli.rs, tests/bdd/steps/locale_resolution.rs, tests/locale_resolution_tests.rs
Use configured or unset-locale constructors in BDD steps and fallback tests.
Validate runtime and construction contracts
tests/locale_stub_strictness_tests.rs, tests/locale_stub_strictness_tests.proptest-regressions, tests/locale_stub_ui_tests.rs, tests/ui/*, docs/developers-guide.md
Test strict reads, unset values, declaration sequences, and the removal of Default construction. Document the same construction and read contracts.

Possibly related issues

  • leynos/netsuke#496: Both changes use explicitly configured environment test doubles and replace implicit environment setup.

Possibly related PRs

Suggested labels: Issue

Suggested reviewers: codescene-access

Poem

Declare each key before it speaks,
Keep unset locales clear and meek.
Strict stubs guard every read,
Builders shape the tests they need.
Fallback paths now run with care.

🚥 Pre-merge checks | ✅ 20
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed Pass because the title accurately describes strict locale stub reads and includes the linked issue reference (#489).
Description check ✅ Passed Pass because the description clearly explains the strict StubEnv changes, design decisions, tests, and verification.
Linked Issues check ✅ Passed Pass because the changes satisfy issue #489 by enforcing declared reads, supporting unset values, reporting offending keys, and removing permissive construction.
Out of Scope Changes check ✅ Passed Pass because the implementation, tests, UI fixtures, regression seed, and documentation directly support the strict StubEnv objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Testing (Overall) ✅ Passed Tests cover undeclared-read panics and key diagnostics, set/unset values, redeclaration order, arbitrary declaration sequences, and compile-time removal of Default with a compiling control fixture.
User-Facing Documentation ✅ Passed Pass this check: the change affects only the internal StubEnv test double and developer documentation; no user-facing behaviour or docs/users-guide.md update is required.
Developer Documentation ✅ Passed The developer guide documents StubEnv's strict states, builders, panic contract, redeclaration semantics, removed Default implementation, and test coverage.
Module-Level Documentation ✅ Passed Retain the module documentation: all PR-relevant Rust modules have //! comments stating their purpose, test utility, and relevant links to locale, CLI, traits, or the harness.
Testing (Unit And Behavioural) ✅ Passed Pass: unit tests cover set, unset, undeclared-key panic, diagnostics, redeclaration, and property invariants; UI tests cover Default; BDD and binary tests cover locale boundaries.
Testing (Property / Proof) ✅ Passed Pass the check: tests/locale_stub_strictness_tests.rs uses proptest over declaration sequences, an independent model, and checks last-write-wins plus undeclared-read panics.
Testing (Compile-Time / Ui) ✅ Passed Accept the check: tests/locale_stub_ui_tests.rs uses direct rustc as the language-specific equivalent, with compile-fail and control fixtures and focused E0599 assertions.
Unit Architecture ✅ Passed StubEnv::var performs a clone-only in-memory query; builders own mutation, while LocaleEnvProvider and SystemLocale remain injected at resolution boundaries and process access stays in `Syste...
Domain Architecture ✅ Passed Keep the change: the diff is limited to test support, tests, and documentation; it adds no domain or production-source dependencies and preserves the LocaleEnvProvider/SystemEnv adapter boundary.
Observability ✅ Passed Treat this as passing: the change affects test-only StubEnv behaviour, not production operations; its panic names the unexpected key for direct test diagnosis.
Security And Privacy ✅ Passed Changes are test-only and documentation-only; no secrets or credentials were added, panic output contains only the undeclared key, and UI commands use parameterized Command arguments without shell...
Performance And Resource Use ✅ Passed PASS: The new linear allowlist checks run only in the test stub with a small declared-key set; property cases are bounded to 0..8, and UI tests share one Cargo build and use metadata-only rustc che...
Concurrency And State ✅ Passed StubEnv keeps state in owned maps; the only shared hook is installed once with Once, uses thread-local suppression, and delegates other-thread panics to the prior hook.
Architectural Complexity And Maintainability ✅ Passed Keep the change: the strict builder enforces a real test invariant, while the existing trait remains, no dependency is added, and the custom UI harness documents and verifies its build boundary.
Rust Compiler Lint Integrity ✅ Passed Keep the check passing: the PR adds no broad allow attributes or artificial anchors; its clones support owned trait returns, dual-key storage, or independent property-test state.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-489-make-locale-stub-strict

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Make StubEnv a strict, declarative test double for environment variables and update tests to use its new API so unexpected env reads fail loudly instead of silently returning None.

File-Level Changes

Change Details Files
Make StubEnv a strict, declarative environment stub that panics on unexpected variable reads.
  • Replace single optional locale field with internal maps of allowed keys and their values.
  • Add strict(), with_locale(), without_locale(), with_var(), and allowing() constructors/builders to declare readable variables and their values or unset state.
  • Implement EnvProvider::var to assert that any requested key was explicitly allowed, then return a cloned value if present or None if declared-unset.
  • Document the rationale for strictness and the removal of Default on StubEnv.
test_support/src/locale_stubs.rs
Remove Default for StubEnv and force call sites to choose explicit constructors.
  • Drop Default from StubEnv’s derive list so StubEnv::default() no longer compiles.
  • Rely on without_locale() for the common "no locale set" case to make intent explicit and avoid runtime panics.
test_support/src/locale_stubs.rs
tests/locale_resolution_tests.rs
Update CLI and locale resolution tests to use the new StubEnv API and declare NETSUKE_JSON as an allowed-but-unset variable.
  • Replace direct struct literal construction of StubEnv with map_or_else over world.locale_env, using without_locale() or with_locale() as appropriate.
  • Use allowing(NETSUKE_JSON_ENV) so tests explicitly permit reads of NETSUKE_JSON while treating it as unset.
  • Update locale_resolution unit test to use StubEnv::without_locale() instead of StubEnv::default().
tests/bdd/steps/cli.rs
tests/bdd/steps/locale_resolution.rs
tests/locale_resolution_tests.rs

Assessment against linked issues

Issue Objective Addressed Explanation
#489 Replace locale_resolution::EnvProvider and SystemEnv with mockable::Env and mockable::DefaultEnv, and delete test_support::locale_stubs::StubEnv. The PR explicitly argues against adopting mockable::Env, keeps EnvProvider in production code, and retains StubEnv in test_support, modifying it to be a strict stub instead of deleting it.
#489 Keep the read_env closure seams in output_mode, output_prefs, and theme but derive them from an injected Env so a single MockEnv can drive all four modules (locale, output mode, theme, emoji). The diff only changes the StubEnv implementation and some locale-related tests; there are no changes to output_mode, output_prefs, or theme to introduce an injected Env or to use a shared MockEnv across modules.
#489 Update localization-related tests (e.g., tests/localization_tests.rs) so they use a single MockEnv instead of the hand-rolled stub and OnceLock, enabling one mock to drive locale, output-mode, theme, and emoji resolution. The tests are updated to use the new strict StubEnv API (e.g., StubEnv::with_locale, StubEnv::without_locale, .allowing(...)), not to use mockable::MockEnv or a shared mock across all modules. The hand-rolled stub design is refined, not replaced by MockEnv.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review August 2, 2026 07:14

@sourcery-ai sourcery-ai Bot 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.

Hey - I've left some high level feedback:

  • StubEnv currently duplicates key storage in both allowed: Vec<String> and values: HashMap<String, String>; consider representing allowed variables in a single map (e.g., HashMap<String, Option<String>>) to avoid duplication and simplify lookups.
  • The var implementation does a linear search over allowed on every read; switching allowed to a HashSet<String> would keep the strictness while making intent clearer and lookups more efficient.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- StubEnv currently duplicates key storage in both `allowed: Vec<String>` and `values: HashMap<String, String>`; consider representing allowed variables in a single map (e.g., `HashMap<String, Option<String>>`) to avoid duplication and simplify lookups.
- The `var` implementation does a linear search over `allowed` on every read; switching `allowed` to a `HashSet<String>` would keep the strictness while making intent clearer and lookups more efficient.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@coderabbitai coderabbitai Bot added the Issue label Aug 2, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 107a6d5d33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +31 to +33
/// Create a stub declaring nothing; every read panics until one is added.
#[must_use]
pub fn strict() -> Self {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add examples for the new StubEnv builders

The new public strict, without_locale, with_var, and allowing methods have only prose descriptions, leaving their composition and outcomes undocumented despite the repository requirement that function documentation include clear usage and outcome examples. Add Rustdoc examples for the newly introduced builders.

AGENTS.md reference: AGENTS.md:L27-L30

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline test_support/src/locale_stubs.rs --items all --type function,struct,impl --match 'StubEnv|EnvProvider' --view expanded
printf '\n--- StubEnv implementation and EnvProvider contract ---\n'
sed -n '20,155p' test_support/src/locale_stubs.rs

Length of output: 662


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline test_support/src/locale_stubs.rs --items all --type struct,function --match 'StubEnv|strict|without_locale|with_var|allowing|var' --view expanded
printf '\n--- StubEnv implementation and EnvProvider contract ---\n'
sed -n '20,155p' test_support/src/locale_stubs.rs

Length of output: 5549


@pandalump, this is resolved.

The current test_support/src/locale_stubs.rs adds Rustdoc examples for all requested builders:

  • StubEnv::strict() shows that an undeclared read panics.
  • StubEnv::without_locale() shows an allowed unset locale returns None.
  • StubEnv::with_var() shows that it overrides a prior allowing() call.
  • StubEnv::allowing() shows that it clears a prior value and returns None.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- codegraph: StubEnv public builder map ---'
ast-grep outline test_support/src/locale_stubs.rs \
  --items all \
  --type struct,function,impl \
  --match 'StubEnv|strict|with_locale|without_locale|with_var|allowing' \
  --view expanded

printf '%s\n' '--- source: Rustdoc and implementations for all requested builders ---'
sed -n '24,140p' test_support/src/locale_stubs.rs

Length of output: 845


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- codegraph: StubEnv public builder map ---'
ast-grep outline test_support/src/locale_stubs.rs \
  --items all \
  --type struct,function \
  --match 'StubEnv|strict|with_locale|without_locale|with_var|allowing' \
  --view expanded

printf '%s\n' '--- source: Rustdoc and implementations for all requested builders ---'
sed -n '24,140p' test_support/src/locale_stubs.rs

Length of output: 4946


@pandalump, this is resolved.

Codegraph analysis confirms that test_support/src/locale_stubs.rs contains Rustdoc examples for every requested builder:

  • StubEnv::strict() shows an undeclared read that panics.
  • StubEnv::without_locale() shows an allowed unset locale that returns None.
  • StubEnv::with_var() shows that it overrides allowing() for the same key.
  • StubEnv::allowing() shows that it clears a previously configured value.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Codegraph: StubEnv public builder methods ---'
ast-grep outline test_support/src/locale_stubs.rs \
  --items all \
  --type struct,impl,function \
  --match 'StubEnv|strict|without_locale|with_var|allowing' \
  --view expanded

printf '%s\n' '--- Rustdoc and implementations ---'
sed -n '24,140p' test_support/src/locale_stubs.rs

Length of output: 806


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Codegraph: StubEnv declarations ---'
ast-grep outline test_support/src/locale_stubs.rs \
  --items all \
  --type struct,function \
  --match 'StubEnv|strict|without_locale|with_var|allowing' \
  --view expanded

printf '%s\n' '--- Rustdoc and builder implementations ---'
sed -n '24,140p' test_support/src/locale_stubs.rs

Length of output: 4794


@pandalump, this is resolved.

Codegraph analysis confirms that test_support/src/locale_stubs.rs defines all four requested builders and gives each one a Rustdoc example:

  • StubEnv::strict() shows an undeclared read that panics.
  • StubEnv::without_locale() shows an allowed unset locale that returns None.
  • StubEnv::with_var() shows that the later value overrides allowing().
  • StubEnv::allowing() shows that it clears a previous value and returns None.

You are interacting with an AI system.

Comment on lines +77 to +83
assert!(
self.allowed.iter().any(|allowed| allowed == key),
"StubEnv was asked for {key:?}, which the test did not declare. \
Declare it with `.with_var(..)` or `.allowing(..)` if the read is \
intended; otherwise the code under test is reading a variable the \
test does not know about."
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add a regression test for undeclared reads

No test introduced by this change calls var with an undeclared key and verifies that it panics; the existing locale-resolution tests exercise only declared reads. Consequently, relaxing or accidentally removing this assertion would leave every test passing while restoring the exact permissive behaviour this commit is intended to fix. Add a focused unit test for the unexpected-read path.

AGENTS.md reference: AGENTS.md:L64-L70

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current commit =='
git rev-parse HEAD

printf '%s\n' '== Rust source map for StubEnv module =='
ast-grep outline test_support/src/locale_stubs.rs --items all

printf '%s\n' '== Test-support source map =='
ast-grep outline test_support --items all --type function

printf '%s\n' '== StubEnv construction and undeclared-read test candidates =='
rg -n --glob '*.rs' -C 4 \
  '(StubEnv::(strict|with_locale|without_locale)|\.var\(".*"\)|should_panic|assert_panics|did not declare|unexpected.*read)' \
  test_support tests

printf '%s\n' '== Attribute-to-function codegraph candidates =='
ast-grep run --lang rust --pattern '#[should_panic]
fn $NAME($$$) { $$$ }' test_support tests || true

Length of output: 17935


@pandalump, treat this as resolved.

The latest commit, 69364c6cb4f31b23de42505c393ac41d1577d710, adds tests/locale_stub_strictness_tests.rs.

undeclared_read_panics calls StubEnv::strict().var("SOME_UNDECLARED_VARIABLE") and requires the undeclared-read panic. the_panic_names_the_offending_key also verifies that the panic includes the unexpected variable name. This covers the regression path from the original finding.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current revision =='
git rev-parse HEAD

printf '%s\n' '== Relevant Rust symbol map =='
ast-grep outline test_support/src/locale_stubs.rs --items all
ast-grep outline tests/locale_stub_strictness_tests.rs --items all

printf '%s\n' '== Undeclared-read test functions =='
ast-grep run --lang rust \
  --pattern '#[should_panic]
fn $NAME($$$) {
  $$$
}' \
  tests/locale_stub_strictness_tests.rs || true

printf '%s\n' '== Focused codegraph edges =='
rg -n -C 5 --glob '*.rs' \
  'undeclared_read_panics|the_panic_names_the_offending_key|StubEnv::strict\(\)\.var\(|fn var\(&self, key: &str\)|did not declare' \
  test_support/src/locale_stubs.rs tests/locale_stub_strictness_tests.rs

Length of output: 5274


@pandalump, mark this finding as resolved.

Codegraph analysis of revision 467df257caef2b0a7165910c7f9522c42def3006 confirms the required regression coverage.

tests/locale_stub_strictness_tests.rs contains undeclared_read_panics. The test calls StubEnv::strict().var("SOME_UNDECLARED_VARIABLE") and requires a panic containing "which the test did not declare".

The call reaches StubEnv’s EnvProvider::var implementation in test_support/src/locale_stubs.rs. That implementation asserts that the key was declared before it returns a value.

the_panic_names_the_offending_key also verifies that the panic contains the undeclared key. This covers both the unexpected-read path and its diagnostic.


🧠 Learnings used
Learnt from: leynos
Repo: leynos/netsuke PR: 502
File: tests/locale_stub_strictness_tests.rs:13-13
Timestamp: 2026-08-04T13:12:33.692Z
Learning: In leynos/netsuke Rust code, do not require wrapping a bare `Option<T>` expression statement in `drop(...)`; `Option<T>` is not `#[must_use]` on the project toolchain, while `Result<T, E>` is. Expression statements still evaluate their calls, including calls in `#[should_panic]` tests. Use `drop(...)` only when the discarded value's type is actually `#[must_use]` and the value is intentionally ignored.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current revision =='
git rev-parse HEAD

printf '%s\n' '== Relevant symbol maps =='
ast-grep outline test_support/src/locale_stubs.rs --items all
ast-grep outline tests/locale_stub_strictness_tests.rs --items all

printf '%s\n' '== Undeclared-read test and implementation edges =='
rg -n -C 6 --glob '*.rs' \
  'fn undeclared_read_panics\b|fn the_panic_names_the_offending_key\b|StubEnv::strict\(\)\.var\(|fn var\(&self, key: &str\)|which the test did not declare|self\.allowed\.iter\(\)\.any' \
  test_support/src/locale_stubs.rs tests/locale_stub_strictness_tests.rs

printf '%s\n' '== Structural test candidates =='
ast-grep run --lang rust \
  --pattern '#[should_panic(expected = $MESSAGE)]
fn $NAME($$$) {
  $$$
}' \
  tests/locale_stub_strictness_tests.rs || true

Length of output: 7382


@pandalump, mark this finding as resolved.

Codegraph analysis of revision efbc241c812ac8ef56c81c09aae68a1a182c770e confirms the regression coverage.

undeclared_read_panics calls StubEnv::strict().var("SOME_UNDECLARED_VARIABLE"). It requires a panic containing "which the test did not declare".

That call reaches LocaleEnvProvider for StubEnv::var. The implementation asserts that the key is declared before it returns a value.

the_panic_names_the_offending_key also verifies that the panic contains the undeclared key.


🧠 Learnings used
Learnt from: leynos
Repo: leynos/netsuke PR: 502
File: tests/locale_stub_strictness_tests.rs:13-13
Timestamp: 2026-08-04T13:12:33.692Z
Learning: In leynos/netsuke Rust code, do not require wrapping a bare `Option<T>` expression statement in `drop(...)`; `Option<T>` is not `#[must_use]` on the project toolchain, while `Result<T, E>` is. Expression statements still evaluate their calls, including calls in `#[should_panic]` tests. Use `drop(...)` only when the discarded value's type is actually `#[must_use]` and the value is intentionally ignored.

You are interacting with an AI system.

Comment thread tests/bdd/steps/cli.rs Outdated
.locale_env
.get()
.map_or_else(StubEnv::without_locale, StubEnv::with_locale)
.allowing(netsuke::locale_resolution::NETSUKE_JSON_ENV);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not whitelist JSON in locale-only tests

In apply_cli, this stub is passed only to resolve_startup_locale, which currently reads NETSUKE_LOCALE; parsing and configuration merging do not receive it. Allowing NETSUKE_JSON therefore means an accidental JSON-variable read added to locale resolution would silently pass, defeating the strictness this change introduces. Remove this allowance unless the helper actually invokes and asserts resolve_startup_json; the identical allowance in the locale-resolution BDD step should be removed as well.

AGENTS.md reference: AGENTS.md:L64-L70

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current commit =='
git rev-parse HEAD

printf '%s\n' '== Relevant module outlines =='
ast-grep outline tests/bdd/steps/cli.rs --items all --type function --match 'apply_cli'
ast-grep outline tests/bdd/steps/locale_resolution.rs --items all --type function
ast-grep outline src --items all --type function --match 'resolve_startup_locale'

printf '%s\n' '== NETSUKE_JSON_ENV allowlists and locale-resolution call sites =='
rg -n -C 4 \
  '(?:\.allowing\s*\(\s*(?:netsuke::)?locale_resolution::NETSUKE_JSON_ENV\s*\)|resolve_startup_locale\s*\()' \
  tests test_support src

printf '%s\n' '== Stub environment read implementation =='
stub_file="$(fd -a '^locale_stubs\.rs$' test_support | head -n 1)"
ast-grep outline "$stub_file" --items all --type function,impl --match 'StubEnv|get'
rg -n -C 5 'fn\s+get\s*\(|impl\s+.*EnvProvider|NETSUKE_JSON_ENV|NETSUKE_LOCALE' "$stub_file"

Length of output: 9228


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current relevant BDD constructors =='
sed -n '35,70p' tests/bdd/steps/cli.rs
sed -n '78,90p' tests/bdd/steps/locale_resolution.rs

printf '%s\n' '== Startup locale call graph =='
ast-grep outline src/locale_resolution.rs --items all --type function --match 'resolve_startup_locale|locale_from'
sed -n '129,180p' src/locale_resolution.rs
rg -n -C 3 '\bget\s*\(' src/locale_resolution.rs

printf '%s\n' '== StubEnv provider implementation =='
stub_file="$(fd -a '^locale_stubs\.rs$' test_support | head -n 1)"
ast-grep outline "$stub_file" --items all --type function --match 'get|with_locale|without_locale|allowing'
rg -n -C 6 'impl\s+.*EnvProvider|fn\s+get\s*\(' "$stub_file"

printf '%s\n' '== Full current NETSUKE_JSON_ENV reference set =='
rg -n -C 2 '\bNETSUKE_JSON_ENV\b' .

Length of output: 5122


@pandalump, this is resolved in commit 69364c6cb4f31b23de42505c393ac41d1577d710.

The codegraph shows that both BDD paths now construct StubEnv without .allowing(NETSUKE_JSON_ENV):

  • tests/bdd/steps/cli.rs::apply_cli
  • tests/bdd/steps/locale_resolution.rs::resolve_startup_locale

locale_resolution::resolve_startup_locale reads only NETSUKE_LOCALE_ENV. NETSUKE_JSON_ENV is read only by the separate resolve_startup_json function. An unexpected JSON read during startup locale resolution will now fail through the strict stub.


🧬 Code Graph Analysis Results

tests/bdd/steps/cli_verify.rs

  • Lines 52–60 — verify_command
    Retrieves the parsed command through get_command, verifies it matches the expected Build, Clean, Graph, or Generate variant, and returns an error if it does not.

  • Lines 76–79 — verify_parsing_succeeded
    Confirms that CLI parsing populated world.cli; otherwise returns an assertion error.

  • Lines 81–87 — verify_error_returned
    Confirms that CLI parsing stored an error in world.cli_error; otherwise returns an assertion error.

  • Lines 132–161 — CLI network policy verification
    Retrieves the parsed CLI network policy, parses the supplied URL, and verifies either that evaluation succeeds or that it fails with an error containing the expected normalized message.

  • Lines 163–177 — Graph and generate argument verification
    Extracts graph or generate command arguments and verifies their optional output paths; graph verification also confirms that --html is set.

  • Lines 179–192 — verify_error_contains
    Retrieves the stored CLI parsing error, normalizes Fluent isolate characters, and checks that the expected fragment occurs in the error message.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current commit =='
git rev-parse HEAD

printf '%s\n' '== Relevant function outlines =='
ast-grep outline tests/bdd/steps/cli.rs --items all --type function --match 'apply_cli'
ast-grep outline tests/bdd/steps/locale_resolution.rs --items all --type function --match 'resolve_startup_locale'
ast-grep outline src/locale_resolution.rs --items all --type function --match 'resolve_startup_locale|resolve_startup_json'

printf '%s\n' '== BDD StubEnv construction and JSON allowlists =='
rg -n -C 5 \
  '(?:StubEnv::(?:with_locale|without_locale)|\.allowing\s*\(\s*(?:netsuke::)?locale_resolution::NETSUKE_JSON_ENV\s*\)|resolve_startup_locale\s*\()' \
  tests/bdd/steps

printf '%s\n' '== Environment reads in startup resolution functions =='
rg -n -C 6 \
  'fn\s+resolve_startup_(?:locale|json)\b|\.get\s*\(|NETSUKE_(?:LOCALE|JSON)_ENV' \
  src/locale_resolution.rs

printf '%s\n' '== StubEnv strict-read implementation =='
stub_file="$(fd -a '^locale_stubs\.rs$' test_support | head -n 1)"
ast-grep outline "$stub_file" --items all --type function,impl --match 'StubEnv|get'
rg -n -C 6 'fn\s+get\s*\(|fn\s+allowing\s*\(|impl\s+.*EnvProvider' "$stub_file"

Length of output: 7959


@pandalump, confirm this review comment is resolved in commit 467df257caef2b0a7165910c7f9522c42def3006.

Codegraph analysis shows that both BDD paths now create StubEnv without .allowing(NETSUKE_JSON_ENV):

  • tests/bdd/steps/cli.rs::apply_cli
  • tests/bdd/steps/locale_resolution.rs::resolve_startup_locale

src/locale_resolution.rs::resolve_startup_locale reads only NETSUKE_LOCALE_ENV. The separate resolve_startup_json function reads NETSUKE_JSON_ENV.

An unexpected JSON environment-variable read in either locale-resolution path will now fail through the strict stub.


🧬 Code Graph Analysis Results

tests/bdd/steps/cli_verify.rs

  • Lines 52–60 — verify_command: Retrieves the parsed command from TestWorld, checks it against the expected Build, Clean, Graph, or Generate variant, and returns an error on mismatch.
  • Lines 76–87 — parsing status checks: verify_parsing_succeeded confirms parsed CLI state exists; verify_error_returned confirms a parsing error was stored.
  • Lines 132–161 — CLI network policy checks: Builds the CLI network policy, parses the URL, and verifies either successful evaluation or rejection with a normalized error-message fragment.
  • Lines 163–177 — graph/generate output checks: Extracts command-specific arguments and verifies configured output paths and the graph HTML flag.
  • Lines 179–192 — verify_error_contains: Retrieves the stored CLI parsing error, normalizes locale isolation characters, and verifies the expected fragment is present.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Checked-out commit =='
git rev-parse HEAD

printf '%s\n' '== Relevant BDD step functions =='
ast-grep outline tests/bdd/steps/cli.rs --items all --type function --match 'apply_cli'
ast-grep outline tests/bdd/steps/locale_resolution.rs --items all --type function --match 'resolve_startup_locale'

printf '%s\n' '== Startup resolution functions =='
ast-grep outline src/locale_resolution.rs --items all --type function --match 'resolve_startup_locale|resolve_startup_json'

printf '%s\n' '== StubEnv construction and JSON allowlists in BDD steps =='
rg -n -C 5 \
  '(?:StubEnv::(?:with_locale|without_locale)|\.allowing\s*\(\s*(?:netsuke::)?locale_resolution::NETSUKE_JSON_ENV\s*\)|resolve_startup_locale\s*\()' \
  tests/bdd/steps

printf '%s\n' '== Environment-key reads in startup locale and JSON resolution =='
rg -n -C 6 \
  'fn\s+resolve_startup_(?:locale|json)\b|\.get\s*\(|NETSUKE_(?:LOCALE|JSON)_ENV' \
  src/locale_resolution.rs

printf '%s\n' '== StubEnv strict read implementation =='
stub_file="$(fd -a '^locale_stubs\.rs$' test_support | head -n 1)"
ast-grep outline "$stub_file" --items all --type function,impl --match 'StubEnv|get'
rg -n -C 6 'fn\s+get\s*\(|fn\s+allowing\s*\(|impl\s+.*EnvProvider' "$stub_file"

Length of output: 8255


@pandalump, confirm this review comment is resolved in commit efbc241c812ac8ef56c81c09aae68a1a182c770e.

Codegraph analysis confirms these conditions:

  • tests/bdd/steps/cli.rs::apply_cli does not call .allowing(NETSUKE_JSON_ENV).
  • tests/bdd/steps/locale_resolution.rs::resolve_startup_locale does not call .allowing(NETSUKE_JSON_ENV).
  • src/locale_resolution.rs::resolve_startup_locale reads only NETSUKE_LOCALE_ENV.
  • src/locale_resolution.rs::resolve_startup_json is the separate function that reads NETSUKE_JSON_ENV.

Keep the JSON variable undeclared in the locale-only BDD paths. The strict stub will now fail if locale resolution reads NETSUKE_JSON_ENV unexpectedly.


🧬 Code Graph Analysis Results

tests/bdd/steps/cli_verify.rs

  • Lines 52–60 — verify_command
    Retrieves the parsed command from TestWorld and verifies it matches the expected Build, Clean, Graph, or Generate variant; returns an error if parsing state or command is missing, or if the variant differs.

  • Lines 76–87 — parsing result verification
    verify_parsing_succeeded checks that a parsed CLI exists. verify_error_returned checks that parsing produced an error.

  • Lines 132–161 — CLI network policy verification
    Retrieves the CLI network policy, parses the supplied URL, and verifies either successful evaluation or rejection with a normalized error-message fragment. URL parsing and unexpected policy results return errors.

  • Lines 179–192 — verify_error_contains
    Retrieves the stored CLI parsing error and verifies its normalized text contains the expected fragment; returns errors when no parsing error exists or the fragment is absent.

You are interacting with an AI system.

Comment thread test_support/src/locale_stubs.rs Outdated
Comment on lines +69 to +70
pub fn allowing(mut self, key: impl Into<String>) -> Self {
self.allowed.push(key.into());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make allowing clear an existing value

Because these builders are composable, StubEnv::strict().with_var("X", "set").allowing("X") is documented as reporting X as unset but still returns Some("set"), since allowing never removes the existing map entry. Make the later builder call determine the declared state, or reject duplicate declarations, and cover both builder orderings as edge cases.

AGENTS.md reference: AGENTS.md:L204-L206

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Current commit =='
git rev-parse HEAD

printf '%s\n' '== StubEnv structure =='
ast-grep outline test_support/src/locale_stubs.rs --items all --view expanded

printf '%s\n' '== Builder implementation and coverage references =='
rg -n -C 5 'fn (with_var|allowing)|StubEnv::strict\(\).*?(with_var|allowing)|with_var\("X"|allowing\("X"' \
  test_support tests

printf '%s\n' '== Test module candidates =='
fd -t f . test_support tests | rg '(locale|stub)'

Length of output: 6506


@pandalump, this is resolved in commit 69364c6cb4f31b23de42505c393ac41d1577d710.

StubEnv::allowing now removes an existing value with self.values.remove(&key) in test_support/src/locale_stubs.rs Line 131.

tests/locale_stub_strictness_tests.rs verifies both builder orderings:

  • with_var("X", "set").allowing("X") returns None.
  • allowing("X").with_var("X", "set") returns Some("set").

Mark this review thread as resolved.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== checked-out commit =='
git rev-parse HEAD

printf '%s\n' '== StubEnv codegraph =='
ast-grep outline test_support/src/locale_stubs.rs --items all --view expanded

printf '%s\n' '== allowing implementation =='
ast-grep run \
  --lang rust \
  --pattern $'pub fn allowing(mut self, key: impl Into<String>) -> Self {\n  $$$\n}' \
  test_support/src/locale_stubs.rs

printf '%s\n' '== strictness-test codegraph =='
ast-grep outline tests/locale_stub_strictness_tests.rs --items all --view expanded

printf '%s\n' '== declaration-order test call sites =='
rg -n -C 4 \
  'StubEnv::strict\(\)\.(with_var|allowing)\("X",?|"X", "set"\)\.(with_var|allowing)\(' \
  tests/locale_stub_strictness_tests.rs || true

Length of output: 3460


@pandalump, mark this thread as resolved.

Codegraph analysis at commit 467df257caef2b0a7165910c7f9522c42def3006 confirms the fix:

  • StubEnv::allowing removes the existing value at test_support/src/locale_stubs.rs Line 131.
  • tests/locale_stub_strictness_tests.rs tests both builder orders:
    • with_var("X", "set").allowing("X") returns None.
    • allowing("X").with_var("X", "set") returns Some("set").

🧠 Learnings used
Learnt from: leynos
Repo: leynos/netsuke PR: 502
File: tests/locale_stub_strictness_tests.rs:13-13
Timestamp: 2026-08-04T13:12:33.692Z
Learning: In leynos/netsuke Rust code, do not require wrapping a bare `Option<T>` expression statement in `drop(...)`; `Option<T>` is not `#[must_use]` on the project toolchain, while `Result<T, E>` is. Expression statements still evaluate their calls, including calls in `#[should_panic]` tests. Use `drop(...)` only when the discarded value's type is actually `#[must_use]` and the value is intentionally ignored.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@coderabbitai Has this now been resolved in the latest commit?

Use codegraph analysis to determine your answer.

If this comment is now resolved, please reply confirming this. Otherwise, please provide a follow-up recommendation.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Checked-out commit =='
git rev-parse HEAD

printf '%s\n' '== StubEnv codegraph =='
ast-grep outline test_support/src/locale_stubs.rs --items all --view expanded

printf '%s\n' '== StubEnv::allowing implementation =='
ast-grep run \
  --lang rust \
  --pattern $'pub fn allowing(mut self, key: impl Into<String>) -> Self {\n  $$$\n}' \
  test_support/src/locale_stubs.rs

printf '%s\n' '== Strictness-test codegraph =='
ast-grep outline tests/locale_stub_strictness_tests.rs --items all --view expanded

printf '%s\n' '== Builder declaration-order call sites =='
rg -n -C 4 \
  'StubEnv::strict\(\)\.(with_var|allowing)\("X",?|"X", "set"\)\.(with_var|allowing)\(' \
  tests/locale_stub_strictness_tests.rs || true

Length of output: 3609


@pandalump, mark this thread as resolved.

Codegraph analysis at commit efbc241c812ac8ef56c81c09aae68a1a182c770e confirms the fix.

StubEnv::allowing removes the existing value in test_support/src/locale_stubs.rs Line 131.

tests/locale_stub_strictness_tests.rs covers both builder orders:

  • with_var("X", "set").allowing("X") returns None.
  • allowing("X").with_var("X", "set") returns Some("set").

You are interacting with an AI system.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test_support/src/locale_stubs.rs`:
- Around line 77-82: Update the assertion message in StubEnv’s allowed-variable
check to use concat!() instead of escaped string continuations, preserving the
exact current message text; then run make check-fmt and make lint.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 881a595b-f88e-4ddd-bbac-472c5409b5c0

📥 Commits

Reviewing files that changed from the base of the PR and between 3f84545 and 107a6d5.

📒 Files selected for processing (4)
  • test_support/src/locale_stubs.rs
  • tests/bdd/steps/cli.rs
  • tests/bdd/steps/locale_resolution.rs
  • tests/locale_resolution_tests.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/shared-actions (auto-detected)

Comment thread test_support/src/locale_stubs.rs Outdated
leynos pushed a commit that referenced this pull request Aug 2, 2026
Addresses five review findings on #502.

Codex (P2): `allowing` never removed an existing map entry, so
`with_var("X", "set").allowing("X")` was documented as declaring X unset
yet still answered `Some("set")`. Both builders now let the most recent
declaration for a key win, and both orderings are covered.

Codex (P1): nothing tested the panic. Relaxing or deleting the assertion
would have left every other test passing while restoring the permissive
behaviour this change exists to remove. Adds
`tests/locale_stub_strictness_tests.rs` covering the panic, that it names
the offending key, that declared reads do not panic, both builder
orderings, and repeated declaration.

Codex (P2): the `NETSUKE_JSON` allowance in the two BDD steps was
speculative — neither helper calls `resolve_startup_json`. Keeping it
would have let an accidental JSON read added to locale resolution pass
silently, defeating the strictness. Removed; the suite still passes.

Codex (P1): adds Rustdoc examples to `strict`, `with_locale`,
`without_locale`, `with_var`, and `allowing`.

CodeRabbit: replaces the escaped string continuations in the assertion
message with `concat!()`, per AGENTS.md.

Refs #489, #496.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/users-guide.md`:
- Around line 14-16: Remove the comma after the inline `rust-toolchain.toml`
reference in the sentence describing the dated Rust nightly toolchain, leaving
the essential “because Netsuke builds...” clause directly connected to the
preceding text.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 83e4d836-0c8b-4c36-8b8c-820aeaee2554

📥 Commits

Reviewing files that changed from the base of the PR and between 3f84545 and 69364c6.

📒 Files selected for processing (11)
  • docs/adr-006-adopt-polonius-nightly-toolchain.md
  • docs/developers-guide.md
  • docs/netsuke-design.md
  • docs/polonius.md
  • docs/snapshot-testing-in-netsuke-using-insta.md
  • docs/users-guide.md
  • test_support/src/locale_stubs.rs
  • tests/bdd/steps/cli.rs
  • tests/bdd/steps/locale_resolution.rs
  • tests/locale_resolution_tests.rs
  • tests/locale_stub_strictness_tests.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/shared-actions (auto-detected)

Comment thread docs/users-guide.md Outdated
leynos pushed a commit that referenced this pull request Aug 3, 2026
AGENTS.md requires concat!() over backslash-continued literals; the same
finding was applied to #502 earlier.

Explicit positional arguments rather than inline capture: `concat!`
produces its literal before the format string is parsed for implicit
`{IDENT}` capture, so the inline form fails to compile with "there is no
argument named WORKSPACE_FALLBACK_ENV".

The rendered diagnostic is unchanged, verified by mutating the emitter
and comparing the failure output:

    expected a WARN event naming NETSUKE_WHICH_WORKSPACE and carrying
    "workspace fallback disabled because env var is not valid UTF-8",
    got ["WARN message=MUTATED env=NETSUKE_WHICH_WORKSPACE"]

Refs #487, #496.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
leynos pushed a commit that referenced this pull request Aug 3, 2026
AGENTS.md requires concat!() over backslash-continued literals; the same
finding was applied to #502 earlier.

Explicit positional arguments rather than inline capture: `concat!`
produces its literal before the format string is parsed for implicit
`{IDENT}` capture, so the inline form fails to compile with "there is no
argument named WORKSPACE_FALLBACK_ENV".

The rendered diagnostic is unchanged, verified by mutating the emitter
and comparing the failure output:

    expected a WARN event naming NETSUKE_WHICH_WORKSPACE and carrying
    "workspace fallback disabled because env var is not valid UTF-8",
    got ["WARN message=MUTATED env=NETSUKE_WHICH_WORKSPACE"]

Refs #487, #496.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
leynos pushed a commit that referenced this pull request Aug 3, 2026
Addresses five review findings on #502.

Codex (P2): `allowing` never removed an existing map entry, so
`with_var("X", "set").allowing("X")` was documented as declaring X unset
yet still answered `Some("set")`. Both builders now let the most recent
declaration for a key win, and both orderings are covered.

Codex (P1): nothing tested the panic. Relaxing or deleting the assertion
would have left every other test passing while restoring the permissive
behaviour this change exists to remove. Adds
`tests/locale_stub_strictness_tests.rs` covering the panic, that it names
the offending key, that declared reads do not panic, both builder
orderings, and repeated declaration.

Codex (P2): the `NETSUKE_JSON` allowance in the two BDD steps was
speculative — neither helper calls `resolve_startup_json`. Keeping it
would have let an accidental JSON read added to locale resolution pass
silently, defeating the strictness. Removed; the suite still passes.

Codex (P1): adds Rustdoc examples to `strict`, `with_locale`,
`without_locale`, `with_var`, and `allowing`.

CodeRabbit: replaces the escaped string continuations in the assertion
message with `concat!()`, per AGENTS.md.

Refs #489, #496.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@leynos
leynos force-pushed the issue-489-make-locale-stub-strict branch from 69364c6 to ccd663e Compare August 3, 2026 23:48
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test_support/src/locale_stubs.rs`:
- Line 13: Update the comment describing the permissive alternative to use the
en-GB-oxendict spelling “unrecognized” instead of “unrecognised,” without
changing the surrounding wording or code.

In `@tests/locale_stub_strictness_tests.rs`:
- Line 13: Consume the Option<String> results in both panic tests by wrapping
the calls to StubEnv::strict().var(...) at tests/locale_stub_strictness_tests.rs
lines 13-13 and 19-19 in drop(...), preserving evaluation before each expected
panic and satisfying #[must_use].
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d8507bab-9243-426c-83e6-f2617819e4f5

📥 Commits

Reviewing files that changed from the base of the PR and between 69364c6 and ccd663e.

📒 Files selected for processing (5)
  • test_support/src/locale_stubs.rs
  • tests/bdd/steps/cli.rs
  • tests/bdd/steps/locale_resolution.rs
  • tests/locale_resolution_tests.rs
  • tests/locale_stub_strictness_tests.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/shared-actions (auto-detected)

Comment thread test_support/src/locale_stubs.rs Outdated
Comment thread tests/locale_stub_strictness_tests.rs
leynos pushed a commit that referenced this pull request Aug 4, 2026
codescene-access[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/locale_stub_strictness_tests.rs`:
- Around line 122-125: Remove the process-wide panic-hook replacement around the
catch_unwind probe in the stub.var(key) test; eliminate the
take_hook/set_hook/restore sequence while preserving the existing panic capture
and result assertions. Keep the default hook active, or isolate the probe in a
separate process if suppressing panic output is required.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f45db0d5-67f7-4dfd-9126-ac35b5b3cf9a

📥 Commits

Reviewing files that changed from the base of the PR and between 467df25 and 91fe043.

📒 Files selected for processing (5)
  • tests/locale_stub_strictness_tests.proptest-regressions
  • tests/locale_stub_strictness_tests.rs
  • tests/locale_stub_ui_tests.rs
  • tests/ui/stub_env_default_compile_fail.rs
  • tests/ui/stub_env_strict_compile_pass.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/shared-actions (auto-detected)

Comment thread tests/locale_stub_strictness_tests.rs Outdated
Comment on lines +122 to +125
let prior = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let read = catch_unwind(AssertUnwindSafe(|| stub.var(key)));
std::panic::set_hook(prior);

@coderabbitai coderabbitai Bot Aug 5, 2026

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- target file ---'
sed -n '1,180p' tests/locale_stub_strictness_tests.rs

printf '%s\n' '--- panic-hook usage ---'
rg -n --glob '*.rs' 'take_hook|set_hook|catch_unwind|parallel|serial' .

Repository: leynos/netsuke

Length of output: 21423


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '1,180p' tests/locale_stub_strictness_tests.rs
rg -n --glob '*.rs' 'take_hook|set_hook|catch_unwind|parallel|serial' .

Repository: leynos/netsuke

Length of output: 21378


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- test execution configuration ---'
fd -HI -t f '(^|/)(nextest\.toml|Makefile|Cargo\.toml|.*\.toml)$' . | sort | head -80
rg -n -C 4 'nextest|cargo test|test-groups|threads|locale_stub_strictness' Makefile .config .github Cargo.toml 2>/dev/null || true

printf '%s\n' '--- repository guidance on panic hooks and test isolation ---'
sed -n '80,115p' test_support/src/localizer.rs
sed -n '150,180p' test_support/src/env_lock.rs
sed -n '1,110p' test_support/src/http_tests.rs

Repository: leynos/netsuke

Length of output: 14784


🏁 Script executed:

#!/bin/bash
set -eu
fd -HI -t f '(^|/)(nextest\.toml|Makefile|Cargo\.toml|.*\.toml)$' . | sort | head -80
rg -n -C 4 'nextest|cargo test|test-groups|threads|locale_stub_strictness' Makefile .config .github Cargo.toml 2>/dev/null || true
sed -n '80,115p' test_support/src/localizer.rs
sed -n '150,180p' test_support/src/env_lock.rs
sed -n '1,110p' test_support/src/http_tests.rs

Repository: leynos/netsuke

Length of output: 14685


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

target = Path("tests/locale_stub_strictness_tests.rs").read_text()
nextest = Path(".config/nextest.toml").read_text()

tests = re.findall(r'(?m)^\s*fn\s+([A-Za-z0-9_]+)\s*\(', target)
hooks = re.findall(r'(?m)^\s*let\s+(prior|read)\b.*|std::panic::(?:take_hook|set_hook)\b', target)

print("target_tests:", tests)
print("target_hook_operations:", len(re.findall(r'std::panic::(?:take_hook|set_hook)', target)))
print("target_uses_catch_unwind:", "catch_unwind" in target)
print("nextest_process_isolation_claim:", "nextest runs each test" in nextest and "own process" in nextest)
print("nextest_serial_group_binaries:", re.findall(r"binary\(([^)]+)\)", nextest))
print("locale_binary_in_serial_group:", "locale_stub_strictness_tests" in nextest)
PY

Repository: leynos/netsuke

Length of output: 623


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re

target = Path("tests/locale_stub_strictness_tests.rs").read_text()
nextest = Path(".config/nextest.toml").read_text()

print("target_tests:", re.findall(r'(?m)^\s*fn\s+([A-Za-z0-9_]+)\s*\(', target))
print("target_hook_operations:", len(re.findall(r'std::panic::(?:take_hook|set_hook)', target)))
print("target_uses_catch_unwind:", "catch_unwind" in target)
print("nextest_process_isolation_claim:", "nextest runs each test" in nextest and "own process" in nextest)
print("nextest_serial_group_binaries:", re.findall(r"binary\(([^)]+)\)", nextest))
print("locale_binary_in_serial_group:", "locale_stub_strictness_tests" in nextest)
PY

Repository: leynos/netsuke

Length of output: 623


Remove the process-wide panic-hook replacement.

std::panic::set_hook affects all threads in the test process. A parallel panic can lose its diagnostics, and restoring prior can overwrite a concurrent hook change. Keep the default hook or run the probe in an isolated process.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/locale_stub_strictness_tests.rs` around lines 122 - 125, Remove the
process-wide panic-hook replacement around the catch_unwind probe in the
stub.var(key) test; eliminate the take_hook/set_hook/restore sequence while
preserving the existing panic capture and result assertions. Keep the default
hook active, or isolate the probe in a separate process if suppressing panic
output is required.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in 6fa22ce, with a different remedy than suggested: rather than keeping the default hook (which buries genuine failures under 256 cases × 3 keys of panic output) or forking a process per probe (disproportionate for a property test), the hook is now installed exactly once as a wrapper that delegates to the prior hook unless a thread-local flag marks the current thread as inside a probe. That removes both hazards you identified: other threads' panics always reach the original hook (nothing is process-wide silenced), and nothing is ever restored, so there is no take/set window to race a concurrent hook change.

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- current commit ---'
git rev-parse --short HEAD

printf '%s\n' '--- target test structure ---'
ast-grep outline tests/locale_stub_strictness_tests.rs --items all --view expanded || true

printf '%s\n' '--- relevant target section ---'
rg -n -C 12 'thread_local|take_hook|set_hook|catch_unwind|panic_hook|Hook' tests/locale_stub_strictness_tests.rs

printf '%s\n' '--- repository hook and synchronization patterns ---'
rg -n -C 5 --glob '*.rs' 'thread_local!|OnceLock|Once::|take_hook|set_hook|catch_unwind' tests test_support src

Length of output: 17798


@leynos, keep this finding unresolved.

6fa22ce still mutates the process-wide panic hook in install_gated_hook().

Once prevents repeated installation in this module. It does not coordinate with other tests that call std::panic::set_hook. A concurrent test can replace the gated hook after installation. This test can also replace a hook that another test installed.

The thread-local gate prevents suppression of panics from other threads after the wrapper is active. It does not remove global hook ownership or hook-installation conflicts.

Remove the hook replacement and retain the default hook. The existing project guidance in test_support/src/localizer.rs makes the same trade-off: accept deliberate-panic output rather than mutate the process-wide hook.


🧠 Learnings used
Learnt from: leynos
Repo: leynos/netsuke PR: 502
File: tests/locale_stub_strictness_tests.rs:13-13
Timestamp: 2026-08-04T13:12:33.692Z
Learning: In leynos/netsuke Rust code, do not require wrapping a bare `Option<T>` expression statement in `drop(...)`; `Option<T>` is not `#[must_use]` on the project toolchain, while `Result<T, E>` is. Expression statements still evaluate their calls, including calls in `#[should_panic]` tests. Use `drop(...)` only when the discarded value's type is actually `#[must_use]` and the value is intentionally ignored.

You are interacting with an AI system.

leynos and others added 6 commits August 6, 2026 03:56
`StubEnv` answered `None` for every key but `NETSUKE_LOCALE`. Had the
code under test been changed to read a differently-named variable —
through a rename, a typo, or a new precedence rung — the stub would have
quietly answered `None` and the test would still have passed, asserting
nothing about the new read. That is the failure mode a test double
exists to prevent.

The stub now declares which variables it answers and panics on anything
else, naming the unexpected key. Unset-but-expected is a distinct,
declarable state, because an absent variable is a legitimate case to
exercise and must be distinguishable from one the test never anticipated.

`Default` is removed rather than retained. On a strict stub it would mean
"deny every read", so `StubEnv::default()` would compile and then panic
at run time for the common "no locale set" case; requiring
`without_locale()` moves that to a compile error. One call site in
`tests/locale_resolution_tests.rs` relied on it and is updated.

This replaces the originally proposed convergence onto `mockable::Env`.
That would have required moving `mockable` — and `mockall` with it —
from dev-dependencies into the production dependency tree, which is a
poor exchange for deleting a two-method trait. `locale_resolution`'s
bespoke `EnvProvider` is a narrow seam of exactly the shape AGENTS.md
permits and was never in violation.

Closes #489.
Refs #496.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses five review findings on #502.

Codex (P2): `allowing` never removed an existing map entry, so
`with_var("X", "set").allowing("X")` was documented as declaring X unset
yet still answered `Some("set")`. Both builders now let the most recent
declaration for a key win, and both orderings are covered.

Codex (P1): nothing tested the panic. Relaxing or deleting the assertion
would have left every other test passing while restoring the permissive
behaviour this change exists to remove. Adds
`tests/locale_stub_strictness_tests.rs` covering the panic, that it names
the offending key, that declared reads do not panic, both builder
orderings, and repeated declaration.

Codex (P2): the `NETSUKE_JSON` allowance in the two BDD steps was
speculative — neither helper calls `resolve_startup_json`. Keeping it
would have let an accidental JSON read added to locale resolution pass
silently, defeating the strictness. Removed; the suite still passes.

Codex (P1): adds Rustdoc examples to `strict`, `with_locale`,
`without_locale`, `with_var`, and `allowing`.

CodeRabbit: replaces the escaped string continuations in the assertion
message with `concat!()`, per AGENTS.md.

Refs #489, #496.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two commitments made on the review thread are now tests rather than
prose. A property test states the invariant the fixed cases were
instances of: over any sequence of with_var and allowing declarations,
each key answers per its last declaration and undeclared keys panic,
checked against an independent last-write-wins model so a bookkeeping
slip between the stub's values and allowed collections cannot agree
with itself. The mutation used to validate the property (disabling
allowing's value clear) is recorded in the regression seed file.

A compile-fail test proves StubEnv::default() does not compile.
Trybuild cannot drive it: it removes ambient RUSTFLAGS and overrides
workspace build.rustflags, so it would rebuild netsuke without
-Zpolonius=next and reject the POLONIUS() sites (dtolnay/trybuild
issues #315 and #333, both open). Instead the test_support rlib is
built by Cargo, which inherits the ambient flags, and the fixtures are
type-checked directly with rustc against that rlib. A control fixture
using the sanctioned builders guards the wiring: were the --extern or
-L dependency plumbing broken, the rejection would happen for the
wrong reason and the compile-fail case would pass vacuously.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Round feedback on #502, both points taken. The undeclared-key probes
now silence the default panic hook for exactly the catch_unwind call
and restore it immediately: 256 cases times three keys of expected
panic output was burying genuine failures. The two compile-fixture
tests now draw the built rlib from a single #[once] rstest fixture
rather than each invoking Cargo, which contended on the target lock
and repeated finished work; the expectation on the fixture's expect is
scoped to the one statement, since a once fixture cannot return Result.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Post-rebase reconciliation: main renamed the locale trait to
LocaleEnvProvider and the hardened lint suite now denies shadowed
bindings, so the builder's key parameters rebind as name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@leynos
leynos force-pushed the issue-489-make-locale-stub-strict branch from 91fe043 to efbc241 Compare August 6, 2026 01:59
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@buzzybee-df12

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/locale_stub_ui_tests.rs`:
- Around line 48-51: Update the error construction in the test rejection path to
use concat!() for the multi-line message, with an explicit {} placeholder and
stderr supplied as the format argument. Preserve the existing message content
and io::Error::other behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c5fa284f-830d-45e9-8c10-bcb7e3aed6cc

📥 Commits

Reviewing files that changed from the base of the PR and between 8ac4103 and efbc241.

📒 Files selected for processing (9)
  • test_support/src/locale_stubs.rs
  • tests/bdd/steps/cli.rs
  • tests/bdd/steps/locale_resolution.rs
  • tests/locale_resolution_tests.rs
  • tests/locale_stub_strictness_tests.proptest-regressions
  • tests/locale_stub_strictness_tests.rs
  • tests/locale_stub_ui_tests.rs
  • tests/ui/stub_env_default_compile_fail.rs
  • tests/ui/stub_env_strict_compile_pass.rs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • leynos/rstest-bdd (auto-detected)
  • leynos/ortho-config (auto-detected)
  • leynos/whitaker (auto-detected)
  • leynos/shared-actions (auto-detected)

Comment thread tests/locale_stub_ui_tests.rs
Round feedback on #502, all three points taken. The undeclared-key
probes' hook swap is serialized behind a lock: nextest isolates each
test in a process, but the in-process runner used for coverage runs
tests as threads, and an unsynchronized take/set pair could strand
one thread's no-op hook as another's restored state. The UI harness
message uses concat! with an explicit placeholder rather than an
escaped continuation. The developers' guide gains a StubEnv
strictness section covering the trichotomy, last-declaration-wins,
and the compile-time refusal of Default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

The probe silenced the default hook by taking it, installing a no-op,
and restoring the original afterwards. Serializing that swap behind a
lock kept the probes from stranding each other's hook, but the swap is
still process-wide: under the threaded in-process coverage runner a
concurrent test's panic during the window is silenced, and the restore
can overwrite a hook installed by someone else in the meantime.

Install a wrapper exactly once instead, delegating to the prior hook
unless a thread-local flag marks the current thread as inside a probe.
Other threads' panics always reach the original hook, and nothing is
ever restored, so there is no window and no race.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
codescene-access[bot]

This comment was marked as outdated.

@leynos

leynos commented Aug 6, 2026

Copy link
Copy Markdown
Owner Author

Both warning rows are addressed on the pushed head:

  • Developer Documentation: docs/developers-guide.md defines the StubEnv strictness contract (strict builders, declared-but-unset values via allowing, undeclared-read panics, last-declaration-wins precedence, and the removal of Default), as your thread reply on the UI-test finding already acknowledged.
  • Concurrency And State: the take/set/restore swap is gone entirely as of 6fa22ce. A wrapper hook is installed exactly once and delegates to the prior hook unless a thread-local flag marks the current thread as inside a probe — no process-wide silencing window and no restore to race.

A fresh review round is queued against this head.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No quality gates enabled for this code.

@leynos
leynos merged commit cc21592 into main Aug 6, 2026
15 of 17 checks passed
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Changed files relevant to the warnings ==='
git diff --name-status origin/main...HEAD | rg '(^|/)(developers-guide\.md|locale_stubs\.rs|locale_stub_strictness_tests\.rs|locale_stub_ui_tests\.rs)$' || true

echo
echo '=== Documentation section locations ==='
rg -n -C 4 'StubEnv|strict|declared|undeclared|last.declaration|Default' docs/developers-guide.md || true

echo
echo '=== Rust file outlines ==='
ast-grep outline test_support/src/locale_stubs.rs --items all --view expanded || true
ast-grep outline tests/locale_stub_strictness_tests.rs --items all --view expanded || true

echo
echo '=== Panic-hook and synchronization-related code ==='
rg -n -C 8 'set_hook|take_hook|panic::|thread_local|thread_local!|Mutex|OnceLock|PROBE|probe' \
  tests/locale_stub_strictness_tests.rs tests/locale_stub_ui_tests.rs test_support/src/locale_stubs.rs || true

echo
echo '=== Focused strictness test source ==='
cat -n tests/locale_stub_strictness_tests.rs

echo
echo '=== Focused developer guide excerpt ==='
line=$(rg -n -m1 'StubEnv' docs/developers-guide.md | cut -d: -f1 || true)
if [ -n "$line" ]; then
  start=$(( line > 12 ? line - 12 : 1 ))
  end=$(( line + 100 ))
  sed -n "${start},${end}p" docs/developers-guide.md | nl -ba -v "$start"
fi

Length of output: 24199


@leynos: Mark both warnings as resolved.

  • Developer Documentation — resolved. docs/developers-guide.md now has the StubEnv strictness section. It documents the strict builders, declared-but-unset keys through allowing, undeclared-read panics with the key, last-declaration-wins behaviour, and the deliberate removal of Default.

  • Concurrency And State — resolved. tests/locale_stub_strictness_tests.rs no longer swaps and restores the panic hook for each probe. It installs one wrapper hook through Once. The wrapper uses a thread-local SILENCED flag and delegates panics from every other thread to the prior hook. This removes the former process-wide silencing and restore race.

Do not create a follow-up issue. Do not perform further work for these two warnings.

You are interacting with an AI system.

@leynos
leynos deleted the issue-489-make-locale-stub-strict branch August 6, 2026 11:09
@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make the locale test stub strict about unexpected variable reads

3 participants