Skip to content

Auto-generate description labels and extend DEPENDENCIES schema#1133

Merged
sbryngelson merged 12 commits intoMFlowCode:masterfrom
sbryngelson:params
Feb 11, 2026
Merged

Auto-generate description labels and extend DEPENDENCIES schema#1133
sbryngelson merged 12 commits intoMFlowCode:masterfrom
sbryngelson:params

Conversation

@sbryngelson
Copy link
Member

@sbryngelson sbryngelson commented Feb 11, 2026

User description

Note

Medium Risk
Touches core parameter validation and documentation generation logic, so mistakes could reject valid cases or mislead users with incorrect constraints/hints. Scope is contained to tooling/validation (not solver runtime), but behavior changes are user-facing and broad.

Overview
Makes parameter docs and constraint hints schema-driven by extending CONSTRAINTS with value_labels, adding richer DEPENDENCIES (requires_value and when_value), and surfacing these via get_value_label and new ParamDef.hint support.

Centralizes case_validator.py rule extraction into a new shared params/ast_analyzer.py, then updates both gen_case_constraints_docs.py and params/generators/docs_gen.py to use it, producing docs that automatically include validator rules, dependency summaries, and labeled enumerations.

Tightens runtime validation by enforcing value-specific dependencies in params/validate.py, improves case_validator.py error formatting to auto-suggest valid values from CONSTRAINTS, and adds several new explicit constraint checks (e.g., igr_pres_limigr, THINC int_comprecon_type, sigR/sigVqbmm, pre_stresshyperelasticity). Build-system doc targets now depend on case_validator.py/definitions.py as needed to keep generated docs up to date.

Written by Cursor Bugbot for commit 13010c8. Configure here.

Summary by CodeRabbit

  • New Features

    • Schema-driven docs and playbooks with human-readable value labels and contextual hints
    • Constraint analysis surfaced in docs to show precise validation rules
  • Improvements

    • Enhanced parameter validation with value-specific dependency messages and clearer error hints
    • Richer parameter reference and pattern docs showing constraints, dependencies, and recommended values
    • More consistent description fallbacks and automated hint generation
  • Chores

    • Added automated compatibility workflow for toolchain testing (CI)

CodeAnt-AI Description

Use schema-driven value labels and enforce value-specific dependencies; surface both in docs and validation

What Changed

  • Parameter schemas now include human-readable value labels and value-specific dependency rules; documentation and generated descriptions show those labels automatically (e.g., model, solver, time stepper options).
  • Documentation generators use a shared AST analyzer to extract validator rules and now display dependency requirements, recommendations, and labeled choice lists in Model Equations, Riemann solvers, bubble model guidance, and the quick parameter reference.
  • Validation enforces dependencies that require specific values (e.g., when a parameter requires another parameter to equal one of a set of values) and reports clearer, auto-generated "valid values" hints in error messages.
  • Case validator fixes add several concrete constraint checks (examples: igr_pres_lim → igr, int_comp → recon_type, sigR/sigV → qbmm, pre_stress → hyperelasticity) and improve error hint formatting using the schema.

Impact

✅ Clearer validation hints for invalid choices
✅ Richer parameter reference with labeled choices and dependency summaries
✅ Fewer undetected value-specific dependency violations

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

sbryngelson and others added 10 commits February 10, 2026 16:00
- Auto-append value_labels to descriptions in _r(), removing hardcoded
  label parentheticals from _SIMPLE_DESCS (7 entries) and DESCRIPTIONS
  (10 entries). Descriptions now stay in sync with CONSTRAINTS.
- Add requires_value and when_value to DEPENDENCIES schema with full
  validation, error formatting, and dependency checking support.
- Add DEPENDENCIES entries for bubbles_euler (requires model_eqns,
  riemann_solver, avg_state values) and model_eqns (when_value for
  models 2, 3, 4).
- Extend docs_gen.py and gen_case_constraints_docs.py to render new
  schema keys with human-readable labels from get_value_label().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- validate.py: condition_label is Optional[str] (None for when_set)
- definitions.py: add annotations to inner _validate_condition closure
- docs_gen.py: use generic Dict/List types for _format_requires_value
  and _format_condition

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- __init__.py: re-export CONSTRAINTS, DEPENDENCIES, get_value_label
  from the params package (needed by gen_case_constraints_docs.py)
- case_validator.py: replace hardcoded error hints with data-driven
  loop over CONSTRAINTS value_labels
- CMakeLists.txt: add definitions.py to DEPENDS for doc generation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix _get_family() regex to match uppercase (chem_wrt_Y), eliminating
spurious pattern view for the general family. Skip rendering value_labels
in constraints since they are already in descriptions. Add 15 new
DEPENDENCIES entries for stretch, body force, TENO, MUSCL, surface
tension, MHD, relativity, schlieren, CFL, and integral parameters.
Fix all pylint warnings to achieve 10.00/10.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a dedicated Constraints column to the full table view instead of
stuffing dependency info into the Description parenthetically. Reformat
dependency text with backtick-wrapped param names and leading action
(e.g. 'Requires `a_x`, `x_a`, `x_b` when enabled'). Add value
labels to when_value conditions for readability. Add range constraints
for dt, t_stop, t_save, cfl_target, cfl_max, weno_eps, R0ref, sigma,
nb, num_source, num_probes, num_integrals, t_step_save, t_step_print.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the AST analyzer (Rule, CaseValidatorAnalyzer, classify_message,
etc.) from gen_case_constraints_docs.py into a shared module at
params/ast_analyzer.py. Add f-string message extraction (106 previously
invisible rules now captured, 333 total), trigger-param detection via
method guards and condition analysis, and an analyze_case_validator()
convenience function.

docs_gen.py now reads constraints from AST-extracted rules instead of
the manually maintained DEPENDENCIES dict, populating 61% of table rows
with validator constraints. Parameter names in messages are wrapped in
backticks with word-boundary matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Alias map resolves boolean combination assignments (e.g. variable_dt =
cfl_dt or cfl_adap_dt) so prohibit rules using those aliases attribute
constraints to the underlying params. If-guard enrichment adds enclosing
if-condition params (perturb_flow, down_sample, etc.) to rules within
the block. Tag-based fallback labels output flags and DEPENDENCIES info
when no other constraints exist. Coverage rises from 64% to 83%.

Fix Doxygen rendering: compound param names (bub_pp%mu_g, x_output%beg)
wrapped in backticks as a unit, snake_case identifiers backticked even
when not in registry, bare % escaped outside code spans, and % no longer
double-escaped inside backtick code spans. Fix description priority so
hand-curated DESCRIPTIONS dict takes precedence over auto-generated text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Validator: add 5 prohibit() statements for previously unchecked params:
- igr_pres_lim requires igr
- int_comp requires recon_type = 2 (MUSCL)
- pre_stress requires hyperelasticity
- sigR/sigV require qbmm

Docs: extend _format_tag_annotation with reverse dependency lookup,
feature tag labels, and prefix-group labels to cover params with no
AST rules or schema constraints. Coverage 83% → 97% of general-family.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend doc generator with compound-name attribute annotation maps for
indexed parameter families. Covers bc_x/y/z sub-params (GRCBC inflow/
outflow, boundary velocities), patch_bc geometry attributes, simplex
perturbation params, and fluid_pp EOS properties.

BC families: 5% → 100%, simplex_params: 0% → 100%, fluid_pp: 69% → 100%.
Overall pattern-row coverage: 89.4%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move hard-coded annotation dicts (BC, patch_bc, simplex, fluid_pp,
tag labels, prefix labels) from docs_gen.py into definitions.py as
HINTS, TAG_DISPLAY_NAMES, and PREFIX_HINTS. Add `hint` field to
ParamDef and auto-populate it at registration time via _lookup_hint(),
so new params get documented without editing the doc generator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sbryngelson sbryngelson marked this pull request as ready for review February 11, 2026 02:41
Copilot AI review requested due to automatic review settings February 11, 2026 02:41
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

Refactors docs generation to a schema- and AST-driven pipeline: adds an AST analyzer for CaseValidator, exposes CONSTRAINTS/DEPENDENCIES/value-label helpers, enhances parameter schema/hints and validation checks, and updates CMake docs dependencies to include new Python sources for doc generation.

Changes

Cohort / File(s) Summary
Build Configuration
CMakeLists.txt
Added doc-generation dependencies on toolchain/mfc/case_validator.py and toolchain/mfc/params/definitions.py; clarifying comment about AST parsing.
AST Analysis Infrastructure
toolchain/mfc/params/ast_analyzer.py
New AST analyzer: Rule dataclass, CaseValidatorAnalyzer, analyze_case_validator() — parses case_validator.py, extracts rules/triggers, expands loops, builds call graph, and infers stages.
Docs generation (refactor)
toolchain/mfc/gen_case_constraints_docs.py
Replaces in-file AST parsing with calls to analyze_case_validator(); rendering now consumes CONSTRAINTS/DEPENDENCIES and analyzer output to produce data-driven markdown.
Parameter schema & hints
toolchain/mfc/params/definitions.py, toolchain/mfc/params/schema.py
Introduces HINTS, TAG_DISPLAY_NAMES, PREFIX_HINTS, value_labels, get_value_label(), _lookup_hint(), and _init_registry(); adds hint field to ParamDef and extends valid keys (e.g., when_value, requires_value).
Parameter package exports
toolchain/mfc/params/__init__.py
Re-exports CONSTRAINTS, DEPENDENCIES, and get_value_label from definitions.
Parameter docs generation & descriptions
toolchain/mfc/params/generators/docs_gen.py, toolchain/mfc/params/descriptions.py
Docs generator integrated with AST analyzer and registry: improved name/pattern handling, reverse dependency mapping, per-parameter constraint rendering, percent/backtick escaping; get_description() now falls back to registry before naming inference.
Validation & error utilities
toolchain/mfc/case_validator.py, toolchain/mfc/params/errors.py, toolchain/mfc/params/validate.py
case_validator now imports CONSTRAINTS, dynamically discovers logical params, adds gating checks (e.g., igr_pres_limigr, int_comprecon_type, pre_stresshyperelasticity), and uses schema hints for error formatting; added dependency_value_error and when_value handling in validation.
CI workflow
.github/workflows/test-toolchain-compat.yml
Adds a GitHub Actions workflow to test toolchain compatibility across multiple Python versions.

Sequence Diagram

sequenceDiagram
    participant Build as Build (CMake)
    participant DocsGen as Docs Generator
    participant AST as AST Analyzer
    participant Validator as CaseValidator
    participant Schema as Constraint Schema

    Build->>DocsGen: trigger doc build
    DocsGen->>AST: analyze_case_validator(case_validator.py)
    AST->>Validator: parse logic & extract rules
    AST->>Schema: map rules to CONSTRAINTS/DEPENDENCIES
    DocsGen->>Schema: load CONSTRAINTS/DEPENDENCIES/get_value_label
    DocsGen->>DocsGen: render markdown using analyzer + schema
    DocsGen-->>Build: write generated docs

    Validator->>Schema: import CONSTRAINTS/DEPENDENCIES
    Validator->>Validator: perform validation checks (new gates)
    Validator->>Schema: format errors using schema hints
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Suggested labels

Review effort 4/5

Suggested reviewers

  • wilfonba

Poem

🐰 I hopped through code and pulled some strings,

I fetched the rules and mapped the things,
Hints stitched neat from schema's thread,
Docs and checks now gently spread,
A tiny rabbit fixed the docs and sprang ahead.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main focus of the PR: extending DEPENDENCIES schema and auto-generating description labels for parameters.
Description check ✅ Passed The description provides comprehensive coverage of changes, motivation, and impact. It follows the template structure with clear sections explaining what changed and the rationale, though it omits some template checklist items.
Docstring Coverage ✅ Passed Docstring coverage is 86.36% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
.github/workflows/test-toolchain-compat.yml (2)

3-7: Consider adding path filters to reduce unnecessary CI runs.

This workflow will trigger on every PR regardless of what files changed. If you want to save CI minutes, you could scope it to paths that matter (e.g., **.py, mfc.sh, toolchain/**). Not critical since the job is likely fast.


17-25: Consider adding a job timeout.

Without timeout-minutes, a stuck ./mfc.sh init or ./mfc.sh lint will consume CI minutes for up to the GitHub default (6 hours). Adding a reasonable timeout is good hygiene.

Proposed fix
 jobs:
   test-toolchain:
     name: "Python ${{ matrix.python-version }}"
     runs-on: ubuntu-latest
+    timeout-minutes: 15
     strategy:
toolchain/mfc/case_validator.py (1)

1932-1947: Regex compiled on every error for every constraint — consider pre-compiling or caching.

Inside the error-formatting loop, re.search(r'\b' + re.escape(param_name.lower()) + r'\b', err_lower) compiles a new regex pattern for each (error, constraint) pair. With ~30 constraints and potentially many errors, this is quadratic in compilation cost. Since _format_errors is only called on the error path, this is unlikely to be a real performance bottleneck, but pre-compiling the patterns would be cleaner.

Also, param_name.lower() is called but CONSTRAINTS keys are already lowercase (per the definitions file). The .lower() is defensive but harmless.

Optional: pre-compile patterns once
# At module level or as a cached helper:
`@lru_cache`(maxsize=1)
def _constraint_patterns():
    return {
        name: re.compile(r'\b' + re.escape(name.lower()) + r'\b')
        for name in CONSTRAINTS
    }

Then in _format_errors:

-            for param_name, constraint in CONSTRAINTS.items():
-                if not re.search(r'\b' + re.escape(param_name.lower()) + r'\b', err_lower):
+            for param_name, constraint in CONSTRAINTS.items():
+                if not _constraint_patterns()[param_name].search(err_lower):
toolchain/mfc/params/definitions.py (3)

579-591: Type hint is narrower than actual usage — value could be any hashable, not just int.

get_value_label accepts value: int, but value_labels keys could be any type that appears in choices (e.g., floats or strings in the future). The function works correctly regardless due to dict .get(), but the type hint is misleading.

Widen type hint
-def get_value_label(param_name: str, value: int) -> str:
+def get_value_label(param_name: str, value) -> str:

816-835: Auto-appending value labels to description may produce overly long strings.

For parameters with many labeled choices (e.g., riemann_solver with 5 entries), the _r function appends all labels to the description: "Riemann solver (1=HLL, 2=HLLC, 3=Exact, 4=HLLD, 5=Lax-Friedrichs)". This is fine for docs but may clutter short-form display contexts. Since this is primarily used for documentation generation, it's acceptable — just noting the trade-off.


686-701: model_eqns dependency: when_value: {2: {"requires": ["num_fluids"]}} — value 1 is missing.

case_validator.py lines 131–138 show that model_eqns=1 prohibits num_fluids, while model_eqns=2, 3, and 4 all require it. The when_value block captures 2, 3, and 4 but doesn't express the prohibition for model_eqns=1. This isn't a bug (the validator still enforces it), but the dependency schema is incomplete for documentation purposes — generated docs won't mention that model_eqns=1 is incompatible with num_fluids.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request enhances the MFC parameter system with auto-generated documentation labels and extends the DEPENDENCIES schema to support value-specific constraints. The changes improve maintainability and reduce code duplication while making parameter documentation more data-driven.

Changes:

  • Introduces a new AST analyzer module (ast_analyzer.py) that extracts validation rules from case_validator.py for use in documentation generation
  • Extends the DEPENDENCIES schema with when_value (value-specific conditions) and requires_value (required specific values) capabilities
  • Adds value_labels to CONSTRAINTS for human-readable labels on integer choice parameters
  • Implements data-driven documentation generation using schema information and AST-extracted rules
  • Refactors gen_case_constraints_docs.py to use shared AST analyzer instead of duplicated parsing logic

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
toolchain/mfc/params/ast_analyzer.py New module for AST analysis of case_validator.py, extracts prohibit rules with trigger detection and f-string handling
toolchain/mfc/params/definitions.py Extends CONSTRAINTS with value_labels, adds when_value/requires_value to DEPENDENCIES, adds HINTS and TAG_DISPLAY_NAMES dicts
toolchain/mfc/params/validate.py Adds _check_condition helper, implements when_value validation and requires_value error checking
toolchain/mfc/params/errors.py Adds dependency_value_error function for value-mismatch error messages
toolchain/mfc/params/generators/docs_gen.py Implements backtick param wrapping, tag annotations, and AST rule formatting for documentation
toolchain/mfc/params/descriptions.py Changes priority order: hand-curated > patterns > auto-generated > inferred
toolchain/mfc/params/schema.py Adds hint field to ParamDef dataclass
toolchain/mfc/params/init.py Exports CONSTRAINTS, DEPENDENCIES, and get_value_label
toolchain/mfc/case_validator.py Adds new constraint checks, uses CONSTRAINTS for auto-generated error hints
toolchain/mfc/gen_case_constraints_docs.py Refactored to use shared AST analyzer, data-driven model/solver documentation
CMakeLists.txt Adds case_validator.py as dependency for parameters.md generation

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 11 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="toolchain/mfc/params/ast_analyzer.py">

<violation number="1" location="toolchain/mfc/params/ast_analyzer.py:402">
P2: Trigger selection relies on ast.walk, which doesn’t preserve left‑to‑right source order, so the “first parameter” can be incorrect. Use a deterministic pre‑order traversal over iter_child_nodes to match source order.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on March 8

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
toolchain/mfc/params/definitions.py (2)

456-495: ⚠️ Potential issue | 🟠 Major

TRY004 (type error handling) is enforced; use TypeError for type validation.

The ruff.toml enables the TRY rule set (which includes TRY003/TRY004). Per TRY004, type validation errors should raise TypeError, not ValueError. The raises at lines 456-495 (in _validate_constraint) and 513-561 (in _validate_condition) that check isinstance() conditions should be changed from ValueError to TypeError, or suppressed locally with # noqa: TRY004 if ValueError semantics are intentional.


590-679: ⚠️ Potential issue | 🔴 Critical

Fix constraint definitions to match validator semantics.

The CONSTRAINTS dict has two critical inconsistencies with actual validation logic:

  1. weno_order should not include 0 in choices. The validator prohibits it (only allows [1, 3, 5, 7]), Fortran code never handles it, and tests treat it as an invalid mutation. Remove this value unless it's truly valid for MUSCL-mode scenarios.

  2. num_source, num_probes, and num_integrals min constraints are too strict. These should allow 0 when their respective features are disabled:

    • num_source should allow 0 when acoustic_source=F
    • num_probes should allow 0 when probe_wrt=F
    • num_integrals should allow 0 when integral_wrt=F

    Currently the CONSTRAINTS specify unconditional {"min": 1}, but the validator only enforces the requirement conditionally (or not at all for probes/integrals). Either update CONSTRAINTS to reflect conditional semantics or add explicit validator checks.

🤖 Fix all issues with AI agents
In `@toolchain/mfc/gen_case_constraints_docs.py`:
- Line 696: Replace the f-string with a plain string where the f prefix is
unnecessary: change the lines.append call that currently reads
lines.append(f"\n<details>") to use a normal string
(lines.append("\n<details>")) so there are no stray f-strings; locate the call
to lines.append in gen_case_constraints_docs.py (the append call around the
"<details>" literal) and remove the leading f.

In `@toolchain/mfc/params/__init__.py`:
- Around line 28-33: The __all__ export list is unsorted which triggers Ruff
RUF022; reorder the entries in the __all__ list alphabetically (e.g.,
CONSTRAINTS, DEPENDENCIES, GET_VALUE_LABEL as appropriate casing—use the exact
names present: CONSTRAINTS, DEPENDENCIES, REGISTRY, RegistryFrozenError,
ParamDef, ParamType, get_value_label) so the list is sorted, or alternatively
add a targeted noqa for RUF022; update the __all__ declaration in this module to
the sorted order.

In `@toolchain/mfc/params/generators/docs_gen.py`:
- Around line 273-275: The signature of _format_validator_rules uses the Python
3.10 `|` union syntax (Dict[str, list] | None) which will break on Python <3.10
unless you enable postponed evaluation of annotations; update the file to either
replace the union with typing.Optional (e.g., Optional[Dict[str, list]] and add
Optional to the typing imports) or add from __future__ import annotations at the
top of the module so the existing signature remains valid; make the change
around the _format_validator_rules definition and the module imports (adjusting
the import line that currently brings in Dict/list types).
🧹 Nitpick comments (10)
toolchain/mfc/params/errors.py (1)

121-149: Make dependency_value_error signature + output formatting consistent with the other helpers.

Minor polish: give condition a default (None), and consider formatting expected_values via format_value so string values don’t rely on Python’s raw list repr.

Proposed tweak (signature + expected value formatting)
-def dependency_value_error(
-    param: str,
-    condition: Optional[str],
-    required_param: str,
-    expected_values: list,
-    got: Any,
-) -> str:
+def dependency_value_error(
+    param: str,
+    required_param: str,
+    expected_values: List[Any],
+    got: Any,
+    condition: Optional[str] = None,
+) -> str:
@@
-    if condition:
-        return (
-            f"{format_param(param)}{condition} requires {format_param(required_param)} "
-            f"to be one of {expected_values}, got {format_value(got)}"
-        )
+    expected = "[" + ", ".join(format_value(v) for v in expected_values) + "]"
+    if condition:
+        return (
+            f"{format_param(param)}{condition} requires {format_param(required_param)} "
+            f"to be one of {expected}, got {format_value(got)}"
+        )
     return (
         f"{format_param(param)} requires {format_param(required_param)} "
-        f"to be one of {expected_values}, got {format_value(got)}"
+        f"to be one of {expected}, got {format_value(got)}"
     )
toolchain/mfc/params/validate.py (1)

30-38: Nice consolidation in _check_condition; consider treating None values as “unset”.

Right now dependency presence checks use key in params. If callers ever include {"foo": None} you’ll emit “got None” value-mismatch errors instead of a clearer “must be set”. Using params.get(key) is None aligns better with the “to be set” wording.

Proposed tweak (treat None as missing)
 def _check_condition(  # pylint: disable=too-many-arguments,too-many-positional-arguments
@@
     """Check a single condition dict (requires, recommends, requires_value)."""
     if "requires" in condition:
         for req in condition["requires"]:
-            if req not in params:
+            if params.get(req) is None:
                 errors.append(dependency_error(name, req, condition_label))
@@
     if "recommends" in condition:
         for rec in condition["recommends"]:
-            if rec not in params:
+            if params.get(rec) is None:
                 warnings.append(dependency_recommendation(name, rec, condition_label))
@@
     if "requires_value" in condition:
         for req_param, expected_vals in condition["requires_value"].items():
-            if req_param not in params:
+            if params.get(req_param) is None:
                 errors.append(dependency_error(name, req_param, condition_label))
             else:
                 got = params[req_param]
                 if got not in expected_vals:

Also applies to: 99-167

toolchain/mfc/case_validator.py (2)

20-21: Consider mirroring these simple cross-param checks into params/DEPENDENCIES as well.

The new guards (igr_pres_lim → igr, int_comp → recon_type==2, sigR/sigV → qbmm, pre_stress → hyperelasticity) are the kind of relationships that seem reusable by the generic dependency checker too (especially now that requires_value exists). That would give consistent errors whether users run params/validate.py or case_validator.py.

Also applies to: 145-152, 195-202, 392-398, 1216-1222


1929-1953: Dynamic value-label hints are helpful; matching by substring is a bit fuzzy.

if param_name not in err_lower can attach hints for the “wrong” param when an error mentions multiple params (e.g., both riemann_solver and wave_speeds). Not a blocker, but if you see mis-hints in practice, consider extracting the “primary” param from the error prefix instead of scanning all constraints.

toolchain/mfc/params/definitions.py (2)

349-449: HINTS/_lookup_hint: guard re.match(...).group(0) to avoid a hard crash on unexpected names.

This is low-risk, but right now a non-matching prefix/attr would raise AttributeError during registry init (hard to debug given the import-time freeze).

Proposed hardening
-def _lookup_hint(name):
+def _lookup_hint(name: str) -> str:
@@
     family = re.sub(r'[_(].*', '', prefix)  # "bc_x" → "bc", "patch_bc" stays
     if family not in HINTS:
         # Try with underscore-joined prefix: "simplex_params" stays
-        family = re.match(r'^[a-zA-Z_]+', prefix).group(0)
+        m = re.match(r'^[a-zA-Z_]+', prefix)
+        if not m:
+            return ""
+        family = m.group(0)
@@
     # Strip index from attr: "vel_in(1)" → "vel_in"
-    attr = re.match(r'^[a-zA-Z_0-9]+', attr_full).group(0)
+    m = re.match(r'^[a-zA-Z_0-9]+', attr_full)
+    if not m:
+        return ""
+    attr = m.group(0)
     return HINTS[family].get(attr, "")

812-831: _r(..., hint=...) plumbing and value-label suffixing make sense—watch for double-rendering in docs.

Now that description may include a (1=..., 2=...) suffix and docs also have value_labels, ensure docs generation doesn’t show both formats redundantly.

toolchain/mfc/gen_case_constraints_docs.py (1)

669-690: _render_cond_parts closes over dep_parts without binding it as a parameter.

This nested function captures dep_parts from the enclosing scope by reference. It works correctly today because the function is called immediately within the same loop iteration, but it's fragile — if the function were ever stored and called later, it would silently use the wrong list. Passing dep_parts as an explicit argument removes the ambiguity.

♻️ Suggested fix
-        def _render_cond_parts(trigger_str, cond_dict):
-            """Render a condition dict into dep_parts entries."""
+        def _render_cond_parts(trigger_str, cond_dict, dep_parts=dep_parts):
+            """Render a condition dict into dep_parts entries."""

Or, more explicitly, pass dep_parts as a regular argument at each call site:

-        def _render_cond_parts(trigger_str, cond_dict):
+        def _render_cond_parts(trigger_str, cond_dict, dep_parts):
             """Render a condition dict into dep_parts entries."""
             ...

-                _render_cond_parts(trigger, cond)
+                _render_cond_parts(trigger, cond, dep_parts)
         ...
-                _render_cond_parts(f"= {wv_val}", wv_cond)
+                _render_cond_parts(f"= {wv_val}", wv_cond, dep_parts)
toolchain/mfc/params/ast_analyzer.py (3)

115-150: self.in_case_validator is set but never read — dead code.

The attribute is assigned at lines 140 and 148 but no code path ever checks its value.

♻️ Suggested removal
     def __init__(self):
         super().__init__()
-        self.in_case_validator = False
         self.current_method: str | None = None
         ...

     def visit_ClassDef(self, node: ast.ClassDef):
         if node.name == "CaseValidator":
-            self.in_case_validator = True
             # collect methods
             for item in node.body:
                 ...
             # now analyze all methods
             for method in self.methods.values():
                 self._analyze_method(method)
-            self.in_case_validator = False
         else:
             self.generic_visit(node)

54-57: Bare Exception catches in AST unparsing fallbacks.

Both _extract_message (line 56) and _resolve_fstring (line 77) catch all exceptions from ast.unparse. While ast.unparse can fail in various ways, catching the broad Exception could mask unexpected bugs. Consider narrowing to (ValueError, TypeError, AttributeError) — the same set used in coverage.py's _extract_message.

♻️ Suggested narrowing
-                except Exception:  # pylint: disable=broad-except
+                except (ValueError, TypeError, AttributeError):

Also applies to: 75-78


1-7: Consider refactoring coverage.py to use this shared analyzer.

The relevant code snippets show that toolchain/mfc/params_tests/coverage.py has its own AST parsing (extract_constraints_from_validator, extract_check_methods) that performs a subset of what this new module does. Once this lands, coverage.py could delegate to analyze_case_validator() to eliminate the duplicated AST traversal logic.

Not blocking for this PR — just a follow-up opportunity.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@toolchain/mfc/case_validator.py`:
- Around line 1938-1953: The current loop over CONSTRAINTS uses a substring
check ("param_name not in err_lower") which false-matches short names like "m"
or "n"; change the match to look for the parameter quoted or as a whole word
instead (e.g., check for f"'{param_name}'" and f'"{param_name}"' and/or use a
regex word-boundary search for rf"\b{re.escape(param_name)}\b") before
proceeding to build the hint; update the check around the for-loop variables
(param_name, err_lower) accordingly so only true parameter mentions trigger the
value_labels hint appended to lines.

In `@toolchain/mfc/params/ast_analyzer.py`:
- Around line 667-683: The substring check `"must be 1" in text` can over-match
(e.g., "must be 10"); replace it with a word-boundary regex check (e.g., use
re.search(r'\bmust be 1\b', text)) and import re if not present, and apply the
same precise matching approach for any similar exact-value checks in the
conditional block that returns "range" so the function in
toolchain/mfc/params/ast_analyzer.py only classifies exact "must be 1" messages
as range constraints.

In `@toolchain/mfc/params/definitions.py`:
- Around line 429-448: The inline comment in _lookup_hint incorrectly states
that "patch_bc" stays unchanged by the regex r'[_(].*' — update the comment to
accurately describe that the regex strips from the first '_' or '(' (so
"patch_bc" becomes "patch", not unchanged), and note that the subsequent
fallback using re.match and HINTS lookup recovers compound prefixes when needed;
reference _lookup_hint, PREFIX_HINTS, HINTS, and the regex r'[_(].*' when making
the clarification.
- Around line 652-666: Update the min constraints for the time-stepping and
physics parameters in the parameter definitions so they match the stricter
checks in case_validator.py: change "dt", "t_stop", "t_save", "weno_eps", and
"R0ref" from {"min": 0} to a small positive value (e.g. {"min": 1e-16}) and
tighten "cfl_target" from {"min": 0} to {"min": 1e-16} or an appropriate (0,1]
enforcing lower bound; keep "t_step_save" as is. Modify the entries for the keys
dt, t_stop, t_save, cfl_target, weno_eps, and R0ref in the same definitions
block so schema validation aligns with the validator’s strictly positive
requirements.

In `@toolchain/mfc/params/generators/docs_gen.py`:
- Around line 273-274: The function signature for _format_validator_rules uses
PEP 604 union syntax (Dict[str, list] | None) which will fail on Python <3.10;
either add from __future__ import annotations at the top of this file to enable
PEP 604 compat like other modules, or change the annotation to
Optional[Dict[str, list]] and add Optional to the typing imports so
_format_validator_rules and any other annotations in this module are compatible
with older Python versions.
🧹 Nitpick comments (8)
toolchain/mfc/params/errors.py (1)

121-149: Minor type annotation inconsistency: list vs List.

The expected_values parameter uses bare list while the rest of the file uses List[str] from typing (e.g., mutual_exclusion_error at line 168). For consistency within this file, consider using List[Any].

This is purely cosmetic since bare list is valid in Python 3.9+.

toolchain/mfc/params/generators/docs_gen.py (2)

142-166: _backtick_params: split-by-backtick assumes paired backticks.

Steps 2 and 3 (lines 155–164) process every other segment after splitting on `. If an input message has an odd number of backticks (e.g., from step 1 producing an unpaired backtick due to overlapping matches), a text segment inside a code span could get double-wrapped.

This is unlikely with well-formed generated messages, but worth a defensive note if external/user-facing strings ever flow through here.


15-16: Unused noqa directive on line 16.

Ruff correctly identifies that # noqa: F401 is not needed here since the rule isn't enabled. The pylint: disable=unused-import comment is still useful if pylint is run separately — consider removing only the noqa: F401 part.

toolchain/mfc/params/ast_analyzer.py (1)

562-588: Minor: simplify key-existence + truthiness check.

Line 575 can be simplified to avoid the redundant key check flagged by Ruff (RUF019).

♻️ Suggested simplification
-            if node.id in alias_map and alias_map[node.id]:
-                return alias_map[node.id][0]
+            alias_sources = alias_map.get(node.id)
+            if alias_sources:
+                return alias_sources[0]
toolchain/mfc/gen_case_constraints_docs.py (4)

26-35: Remove unused noqa directives.

Ruff reports the noqa: E402 directives at Lines 31 and 32 are unused (E402 is not enabled). These can be cleaned up.

♻️ Suggested cleanup
-from mfc.params import CONSTRAINTS, DEPENDENCIES, get_value_label  # noqa: E402  pylint: disable=wrong-import-position
-from mfc.params.ast_analyzer import (  # noqa: E402  pylint: disable=wrong-import-position
+from mfc.params import CONSTRAINTS, DEPENDENCIES, get_value_label  # pylint: disable=wrong-import-position
+from mfc.params.ast_analyzer import (  # pylint: disable=wrong-import-position

667-691: Move _render_cond_parts outside the loop or pass dep_parts as an argument.

Ruff flags B023 because _render_cond_parts captures dep_parts via closure without binding it as a parameter. While this works correctly here (the function is called immediately within the same iteration), redefining the function on every loop iteration is wasteful and the closure pattern is fragile — a future refactor could easily introduce a deferred-call bug.

♻️ Suggested fix: pass dep_parts as a parameter
-        dep_parts = []
-
-        def _render_cond_parts(trigger_str, cond_dict):
-            """Render a condition dict into dep_parts entries."""
-            if "requires" in cond_dict:
-                dep_parts.append(f"When {trigger_str}, requires: {', '.join(f'`{r}`' for r in cond_dict['requires'])}")
+        dep_parts = []
+
+        def _render_cond_parts(trigger_str, cond_dict, parts):
+            """Render a condition dict into parts entries."""
+            if "requires" in cond_dict:
+                parts.append(f"When {trigger_str}, requires: {', '.join(f'`{r}`' for r in cond_dict['requires'])}")
             if "requires_value" in cond_dict:
                 rv_items = []
                 for rv_p, rv_vs in cond_dict["requires_value"].items():
                     labeled = [f"`{v}` ({get_value_label(rv_p, v)})" for v in rv_vs]
                     rv_items.append(f"`{rv_p}` = {' or '.join(labeled)}")
-                dep_parts.append(f"When {trigger_str}, requires {', '.join(rv_items)}")
+                parts.append(f"When {trigger_str}, requires {', '.join(rv_items)}")
             if "recommends" in cond_dict:
-                dep_parts.append(f"When {trigger_str}, recommends: {', '.join(f'`{r}`' for r in cond_dict['recommends'])}")
+                parts.append(f"When {trigger_str}, recommends: {', '.join(f'`{r}`' for r in cond_dict['recommends'])}")
 
         for cond_key in ["when_true", "when_set"]:
             cond = dep.get(cond_key, {})
             if cond:
                 trigger = "enabled" if cond_key == "when_true" else "set"
-                _render_cond_parts(trigger, cond)
+                _render_cond_parts(trigger, cond, dep_parts)
 
         if "when_value" in dep:
             for wv_val, wv_cond in dep["when_value"].items():
-                _render_cond_parts(f"= {wv_val}", wv_cond)
+                _render_cond_parts(f"= {wv_val}", wv_cond, dep_parts)

Better yet, define _render_cond_parts once before the loop (outside the for param in quick_ref_params: loop).


696-697: Remove extraneous f prefix (F541).

Line 696 has an f-string with no placeholders.

♻️ Fix
-        lines.append(f"\n<details>")
+        lines.append("\n<details>")

611-618: Euler-Lagrange section is hardcoded while Euler-Euler is data-driven.

The Euler-Euler section above (Lines 583-609) reads requirements from DEPENDENCIES, but the Euler-Lagrange section here remains hardcoded. Consider making this consistent by sourcing from the schema as well, or leave a TODO for follow-up.

- docs_gen.py: Render value_labels in Constraints column instead of
  skipping them, fixing 12 params that lost valid-value info
- case_validator.py: Use word-boundary regex for CONSTRAINTS hint
  matching to prevent false matches on short param names
- ast_analyzer.py: Use word-boundary regex for 'must be 1' pattern
  to avoid matching 'must be 10', 'must be 100', etc.
- definitions.py: Fix misleading comment in _lookup_hint()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 11, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai bot added the size:XXL This PR changes 1000+ lines, ignoring generated files label Feb 11, 2026
@codeant-ai
Copy link
Contributor

codeant-ai bot commented Feb 11, 2026

CodeAnt AI finished reviewing your PR.

- Remove overly strict sigR/sigV qbmm checks that broke all test cases
  (BASE_CFG defaults sigR/sigV to 0.1 regardless of qbmm)
- Lowercase constraint keys before matching against lowered error strings
- Guard re.match() calls in _lookup_hint against None returns
- Add Python 3.10-3.14 matrix CI workflow for toolchain compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Feb 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 44.03%. Comparing base (7b35b59) to head (da248d3).
⚠️ Report is 6 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1133   +/-   ##
=======================================
  Coverage   44.03%   44.03%           
=======================================
  Files          70       70           
  Lines       20649    20649           
  Branches     2053     2053           
=======================================
  Hits         9093     9093           
  Misses      10368    10368           
  Partials     1188     1188           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sbryngelson
Copy link
Member Author

/improve

Comment on lines +13 to +16
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Remove the unreleased Python version 3.14 from the CI matrix to prevent workflow failures. [possible issue, importance: 6]

Suggested change
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']

Copy link
Member Author

Choose a reason for hiding this comment

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

Python 3.14 is available in actions/setup-python and passes in CI — all 5 matrix versions (3.10–3.14) are green. No change needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Question:

Python 3.14 is available in actions/setup-python and passes in CI — all 5 matrix versions (3.10–3.14) are green. No change needed.

Answer:

This is a “speculative CI hygiene” suggestion (it assumed 3.14 is unreleased / unsupported), and you can reduce this kind of noise by tightening /improve guidance and/or filtering mid-score suggestions.

Recommended config (reduce speculative CI-matrix/version suggestions unless there’s evidence of failure):

[pr_code_suggestions]
extra_instructions = """\
- Do not suggest changing CI matrices / supported language versions (e.g., Python/Node) unless the PR diff or PR checks show an actual failure/incompatibility.
- Avoid claims like “unreleased/unsupported version” unless you can point to concrete evidence in the PR context.
- If CI is green, do not propose removing versions purely as a preventative measure.
"""

Optional: filter out this class of mid-importance suggestions (the example was importance 6):

[pr_code_suggestions]
suggestions_score_threshold = 7

(This will drop suggestions scored below 7; use carefully since it can also hide other useful items.)

If you prefer to exclude CI/workflow files from suggestions altogether:

[pr_code_suggestions]
extra_instructions = """\
- Ignore changes in CI/workflow files (e.g., .github/workflows/*).
"""

or globally:

[ignore]
glob = [".github/workflows/*"]

If you share which tool produced the comment (/improve vs /review) and your git provider (GitHub/GitLab/etc.), I can tailor the exact config snippet to the right section.

Relevant Sources:

Comment on lines +563 to +589
def _extract_trigger_from_condition(condition: ast.AST, local_param_map: Dict[str, str],
alias_map: Optional[Dict[str, List[str]]] = None) -> Optional[str]:
"""
Fallback trigger detection: walk the condition AST left-to-right,
return the first parameter name found. Resolves aliases to their first source param.
"""
if alias_map is None:
alias_map = {}
# Walk in source order (left-to-right in boolean expressions)
for node in ast.walk(condition):
if isinstance(node, ast.Name):
if node.id in local_param_map:
return local_param_map[node.id]
if node.id in alias_map and alias_map[node.id]:
return alias_map[node.id][0]
if isinstance(node, ast.Call):
if ( # pylint: disable=too-many-boolean-expressions
isinstance(node.func, ast.Attribute)
and isinstance(node.func.value, ast.Name)
and node.func.value.id == "self"
and node.func.attr == "get"
and node.args
and isinstance(node.args[0], ast.Constant)
and isinstance(node.args[0].value, str)
):
return node.args[0].value
return None
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Replace ast.walk() with a deterministic, recursive traversal in _extract_trigger_from_condition to ensure consistent trigger parameter selection for documentation grouping. [possible issue, importance: 8]

Suggested change
def _extract_trigger_from_condition(condition: ast.AST, local_param_map: Dict[str, str],
alias_map: Optional[Dict[str, List[str]]] = None) -> Optional[str]:
"""
Fallback trigger detection: walk the condition AST left-to-right,
return the first parameter name found. Resolves aliases to their first source param.
"""
if alias_map is None:
alias_map = {}
# Walk in source order (left-to-right in boolean expressions)
for node in ast.walk(condition):
if isinstance(node, ast.Name):
if node.id in local_param_map:
return local_param_map[node.id]
if node.id in alias_map and alias_map[node.id]:
return alias_map[node.id][0]
if isinstance(node, ast.Call):
if ( # pylint: disable=too-many-boolean-expressions
isinstance(node.func, ast.Attribute)
and isinstance(node.func.value, ast.Name)
and node.func.value.id == "self"
and node.func.attr == "get"
and node.args
and isinstance(node.args[0], ast.Constant)
and isinstance(node.args[0].value, str)
):
return node.args[0].value
return None
def _extract_trigger_from_condition(condition: ast.AST, local_param_map: Dict[str, str],
alias_map: Optional[Dict[str, List[str]]] = None) -> Optional[str]:
"""
Fallback trigger detection: traverse the condition in a deterministic
left-to-right, source-like order and return the first parameter name found.
"""
if alias_map is None:
alias_map = {}
def _first_param(node: ast.AST) -> Optional[str]:
if isinstance(node, ast.Name):
if node.id in local_param_map:
return local_param_map[node.id]
if node.id in alias_map and alias_map[node.id]:
return alias_map[node.id][0]
return None
if isinstance(node, ast.Call):
if (isinstance(node.func, ast.Attribute)
and isinstance(node.func.value, ast.Name)
and node.func.value.id == "self"
and node.func.attr == "get"
and node.args
and isinstance(node.args[0], ast.Constant)
and isinstance(node.args[0].value, str)):
return node.args[0].value
for arg in node.args:
hit = _first_param(arg)
if hit:
return hit
return None
if isinstance(node, ast.BoolOp):
for v in node.values: # preserves written order
hit = _first_param(v)
if hit:
return hit
return None
if isinstance(node, ast.UnaryOp):
return _first_param(node.operand)
if isinstance(node, ast.Compare):
hit = _first_param(node.left)
if hit:
return hit
for comp in node.comparators:
hit = _first_param(comp)
if hit:
return hit
return None
for child in ast.iter_child_nodes(node):
hit = _first_param(child)
if hit:
return hit
return None
return _first_param(condition)

Copy link
Member Author

Choose a reason for hiding this comment

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

ast.walk() is deterministic (BFS) for a given AST — the result is stable even if not documented as source-order. Rewriting as a recursive traversal adds complexity for no practical benefit.

Copy link
Contributor

Choose a reason for hiding this comment

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

Question:

ast.walk() is deterministic (BFS) for a given AST — the result is stable even if not documented as source-order. Rewriting as a recursive traversal adds complexity for no practical benefit.

Answer:

You can reduce “refactor for theoretical determinism” suggestions by tightening the /improve guidance via pr_code_suggestions.extra_instructions—e.g., require a concrete, user-visible bug or failing test before proposing non-trivial traversal/algorithm rewrites, and prefer minimal changes over speculative robustness improvements.

Add something like this to your repo .pr_agent.toml (or the wiki .pr_agent.toml page in Qodo Merge):

[pr_code_suggestions]
extra_instructions = """\
- Prefer minimal, high-signal fixes. Avoid speculative refactors.
- Do NOT suggest rewriting algorithms/APIs (e.g., replacing ast.walk with custom traversals) unless there is clear evidence of a real bug or instability in this codebase (repro steps, failing test, or documented requirement).
- If the concern is ordering/determinism, first verify whether the current approach is already deterministic for the given input; if yes, do not propose a rewrite—at most suggest adding a clarifying comment or a small test.
- When proposing changes, justify the practical impact (user-visible behavior, correctness, performance, security). If impact is uncertain, skip the suggestion.
"""

If you still get too many medium-value suggestions, you can additionally filter by score, but use it carefully because high thresholds can hide useful findings:

[pr_code_suggestions]
suggestions_score_threshold = 8

Relevant Sources:

Comment on lines +161 to +166
if "when_value" in deps:
for trigger_val, condition in deps["when_value"].items():
if value == trigger_val:
_check_condition(
name, condition, f"={trigger_val}", params, errors, warnings,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: In check_dependencies, normalize the parameter value and the when_value trigger value to strings before comparison to prevent silent failures due to type mismatches. [possible issue, importance: 8]

Suggested change
if "when_value" in deps:
for trigger_val, condition in deps["when_value"].items():
if value == trigger_val:
_check_condition(
name, condition, f"={trigger_val}", params, errors, warnings,
)
if "when_value" in deps:
value_norm = str(value)
for trigger_val, condition in deps["when_value"].items():
if value_norm == str(trigger_val):
_check_condition(
name, condition, f"={trigger_val}", params, errors, warnings,
)

Copy link
Member Author

Choose a reason for hiding this comment

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

Both the when_value keys and the parameter values come from Python source dicts where types match naturally (int literals are ints). Adding str() conversions would mask real bugs if types ever did mismatch. No change needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Question:

Both the when_value keys and the parameter values come from Python source dicts where types match naturally (int literals are ints). Adding str() conversions would mask real bugs if types ever did mismatch. No change needed.

Answer:

You can steer /improve away from “defensive type normalization” suggestions (like adding str() around comparisons) by adding a targeted instruction for code suggestions.

Add this to your repo (or wiki/global) .pr_agent.toml:

[pr_code_suggestions]
extra_instructions = """\
- Do not suggest coercing values to strings (e.g., str(...)) or otherwise normalizing types just to make equality comparisons pass.
- Assume compared values come from typed Python structures where types are intended to match (e.g., dict literals / parsed config).
- Only suggest type conversions when the PR diff shows mixed-type sources or an explicit, documented requirement for string-only comparison.
- Prefer surfacing type mismatches as errors (or adding validation/assertions) rather than masking them via coercion.
"""

If this kind of suggestion still appears too often, consider raising filtering slightly, but be careful not to hide real issues:

[pr_code_suggestions]
suggestions_score_threshold = 1

Relevant Sources:

Comment on lines +118 to +127
if "requires_value" in condition:
for req_param, expected_vals in condition["requires_value"].items():
if req_param not in params:
errors.append(dependency_error(name, req_param, condition_label))
else:
got = params[req_param]
if got not in expected_vals:
errors.append(dependency_value_error(
name, condition_label, req_param, expected_vals, got,
))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: In _check_condition, normalize the parameter value and the expected values in requires_value to strings before comparison to prevent silent failures due to type mismatches. [possible issue, importance: 8]

Suggested change
if "requires_value" in condition:
for req_param, expected_vals in condition["requires_value"].items():
if req_param not in params:
errors.append(dependency_error(name, req_param, condition_label))
else:
got = params[req_param]
if got not in expected_vals:
errors.append(dependency_value_error(
name, condition_label, req_param, expected_vals, got,
))
if "requires_value" in condition:
for req_param, expected_vals in condition["requires_value"].items():
if req_param not in params:
errors.append(dependency_error(name, req_param, condition_label))
else:
got = params[req_param]
got_norm = str(got)
expected_norm = [str(v) for v in expected_vals]
if got_norm not in expected_norm:
errors.append(dependency_value_error(
name, condition_label, req_param, expected_vals, got,
))

Copy link
Member Author

Choose a reason for hiding this comment

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

Same reasoning as above — types match naturally from Python source. str() normalization is unnecessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

Question:

Same reasoning as above — types match naturally from Python source. str() normalization is unnecessary.

Answer:

Add a repo-level instruction for /improve to avoid “defensive type-normalization” suggestions (like str()-casting) when values originate from in-process Python objects and types are expected to be consistent.

[pr_code_suggestions]
extra_instructions = """\
- Assume Python types are intentionally preserved when values originate from internal code (not user input / serialization).
- Do NOT suggest normalizing values via str()/repr() (or similar coercions) just to “avoid type mismatches”.
- Only suggest type conversions if the PR explicitly introduces mixed-type comparisons or data crosses a boundary (CLI/env vars, JSON/YAML, HTTP, DB, etc.).
- Prefer catching true mismatches with explicit validation (type checks) rather than silent coercion.
"""

If you want this guidance to apply more broadly as an organizational rule, place it in your best-practices guidance (local best_practices.md or global best practices) so the model treats it as a consistent convention.

Relevant Sources:

Comment on lines +656 to +663
# Time stepping (must be positive)
"dt": {"min": 0},
"t_stop": {"min": 0},
"t_save": {"min": 0},
"t_step_save": {"min": 1},
"t_step_print": {"min": 1},
"cfl_target": {"min": 0},
"cfl_max": {"min": 0},
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Update the minimum values for time-stepping parameters like dt and t_stop from 0 to a small positive number to enforce that they are strictly positive, matching the comment's intent. [general, importance: 7]

Suggested change
# Time stepping (must be positive)
"dt": {"min": 0},
"t_stop": {"min": 0},
"t_save": {"min": 0},
"t_step_save": {"min": 1},
"t_step_print": {"min": 1},
"cfl_target": {"min": 0},
"cfl_max": {"min": 0},
# Time stepping (must be positive)
"dt": {"min": 1e-16},
"t_stop": {"min": 1e-16},
"t_save": {"min": 0},
"t_step_save": {"min": 1},
"t_step_print": {"min": 1},
"cfl_target": {"min": 1e-16},
"cfl_max": {"min": 1e-16},

Copy link
Member Author

Choose a reason for hiding this comment

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

The CONSTRAINTS dict serves as documentation/hints — the actual enforcement is in case_validator.py. Using min: 1e-16 is an arbitrary approximation of "strictly positive" that doesn't truly represent the constraint. The validator is the source of truth.

Copy link
Contributor

Choose a reason for hiding this comment

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

Question:

The CONSTRAINTS dict serves as documentation/hints — the actual enforcement is in case_validator.py. Using min: 1e-16 is an arbitrary approximation of strictly positive that doesn't truly represent the constraint. The validator is the source of truth.

Answer:

Add a repo-level instruction to stop the /improve suggestions from “enforcing” semantics in documentation-only dicts (like CONSTRAINTS) by inventing sentinel values (e.g., 1e-16) and to treat the validator as the source of truth.

[pr_code_suggestions]
extra_instructions = """\
- When you see configuration/constraints dictionaries that serve as documentation or hints (e.g., CONSTRAINTS), do NOT propose changing numeric bounds to approximate concepts like “strictly positive” (avoid sentinel values like 1e-16).
- Prefer suggesting: (a) align comments/docs with actual enforcement, or (b) update the real validation/enforcement code (e.g., case_validator.py) if behavior should change.
- If enforcement differs from docs, explicitly ask to confirm which is the source of truth before recommending code changes.
"""

If you only want this behavior for automated runs, apply it only in your auto-command (example for GitHub App):

[github_app]
pr_commands = [
  "/improve --pr_code_suggestions.extra_instructions='When constraints dicts are doc-only, do not suggest sentinel min values; prefer changing validator or docs.'",
]

Relevant Sources:

@MFlowCode MFlowCode deleted a comment from codeant-ai bot Feb 11, 2026
@sbryngelson sbryngelson enabled auto-merge (squash) February 11, 2026 19:18
@sbryngelson sbryngelson disabled auto-merge February 11, 2026 19:18
@sbryngelson sbryngelson merged commit afc11b6 into MFlowCode:master Feb 11, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files

Development

Successfully merging this pull request may close these issues.

1 participant