Repository navigation
Share duplicated R-runner, security-filter and schema definitions - #123
Merged
Merged
Conversation
- flexible_r: validate_r_code checked an inline copy of the blocked R patterns while the module-level DANGEROUS_PATTERNS sat unused; it now checks the constant, so there is one list to keep current. - r_integration: the sync and async runners share temp-file setup and cleanup (_r_temp_files), the failure report (_r_failure_report) and the missing-package explanation (_missing_package_message, _PACKAGE_FEATURES). The two diagnosis functions share their initial dict and the parsing of each check's output; the subprocess calls stay separate. - core/schemas: coefficient_inference_schema() and r_squared_schema() for linear_model, panel_regression and instrumental_variables. - fileops: _read_summary_schema() and _write_result_properties(). - visualization: _image_output_properties() and _render_plot(). Drifted copies, now one version each: - missing-package map: the async copy lacked knitr; it has it now. - missing-package message: the async wording (R -e "install.packages(...)") is used by both runners; the sync copy's hard-coded "install all" list of 19 packages is gone. - r_squared description: "R-squared (coefficient of determination)" for linear_model, panel_regression and instrumental_variables. IV keeps its missing lower bound, since a 2SLS R-squared can be negative. tests/unit/tools/test_tool_schema_snapshot.py pins every tool's input and output schema to tests/fixtures/tool_schemas.json, generated from main; the only fixture change from main is the two r_squared descriptions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Removes the copies a jscpd scan found in
rmcp/(renamed-identifier matching, ≥12 lines). jscpd goes from 17 clones to 0. The behavior doesn't change, except in the three places where the copies had already drifted apart (listed below).Most of the
r_integration.pydiff is re-indentation: each runner lost onetry:level once cleanup moved into a context manager. Review it with whitespace hidden (?w=1), which brings the whole PR down to +357/−661.What was duplicated and where it lives now
tools/flexible_r.py42–55 (DANGEROUS_PATTERNS, unused) vs 141–154 (inlineremaining_dangerous, the list actually checked)validate_r_codechecksDANGEROUS_PATTERNS. This is a security filter, and the module constant was dead code: editing it had no effect.r_integration.py268–284 vs 485–503, plus the cleanupfinallyblocks: temp script/args/result files, Windows path normalization, unlink_r_temp_files(args)context manager yielding_RTempFilesr_integration.py314–326 vs 661–673: failure report (R path, env, stdout/stderr)_r_failure_report(...), using_r_environment()r_integration.py335–362 vs 682–711: "there is no package called" handling and the package→feature map_missing_package_message(stderr)+_PACKAGE_FEATURES. The control flow around it stays per runner.r_integration.py1116–1142 vs 1210–1236, 1156–1168 vs 1257–1269: diagnosis dict and parsing of each check's output_new_diagnosis(),_record_r_version(...),_record_jsonlite_test(...),_JSONLITE_TEST_SCRIPT. Thesubprocess.run/asyncio.create_subprocess_execcalls stay separate.tools/econometrics.py40–57, 153–170;tools/regression.py56–73: std_errors / t_values / p_values / r_squaredcore/schemas.py:coefficient_inference_schema(),r_squared_schema(), next totable_schema()/formula_schema()tools/fileops.py79–97 vs 597–615 vs 697–715: readsummaryblock_read_summary_schema(). Each tool keeps its ownrequiredlist.tools/fileops.py142–168 vs 220–246 (and the same props inwrite_json): write-result props_write_result_properties(), spread into each tool's ownproperties.requiredstays per tool.tools/visualization.py78–90 ×6: dimensions / image_data / image_mime_type_image_output_properties().requiredstays per tool.tools/visualization.py102–114 ×6: return_image/width/height +execute_r_script_with_image_async_render_plot(context, params, r_script, default_width, default_height), which keeps each tool's own default size. Path confinement andget_r_scriptstay outside thetryin each tool, as before, so their errors still skip the "creation failed" log.Fragments are functions that return fresh dicts, not shared module-level dicts, so no two tool schemas share a mutable object. This matches the existing helpers in
core/schemas.py.Drifted copies: which version I kept
knitr. Both runners now use the 19-entry map that includes"knitr": "Table Formatting & Reporting".R -e "install.packages('pkg')") for both runners. The sync copy also printed a hard-coded "install all RMCP packages" list of 19 packages. I dropped it: it is a fraction of the 429-package allowlist and was a third copy of the map's keys.r_squareddescription. I kept "R-squared (coefficient of determination)". It replaces "R-squared (overall fit)" inpanel_regressionand "R-squared value" ininstrumental_variables. Only the wording is unified.instrumental_variableskeeps its schema withoutminimum: 0, because a 2SLS R-squared is negative when the instrumented fit is worse than the mean, and adding the bound would make output validation reject valid results. Itsadj_r_squaredwording ("Adjusted R-squared value") is untouched.Schema guard
New
tests/unit/tools/test_tool_schema_snapshot.pycollects every@toolinrmcp.tools(50 tools) and compares each input and output schema withtests/fixtures/tool_schemas.json. I generated the fixture from main before refactoring. A comparison of the main-generated and branch-generated schemas shows exactly two differences, the r_squared descriptions above:I confirmed the test fails when it should: changing one description inside
_read_summary_schema()makes it fail onread_csv. After an intended schema change, regenerate the fixture withuv run python tests/unit/tools/test_tool_schema_snapshot.py.Checks (macOS, R 4.6.0 installed)
uv run ruff check .uv run ruff format --check .uv run pyrightuv run pytestRscript rmcp/r_assets/run_tests.Ruv run --group docs sphinx-build -W -b html docs …uvx --python 3.12 --from preen==0.7.1 preen check --strict --skip testsjscpd (
uvx jscpd==5.4.2 rmcp --format python --ignore-identifiers=consistent --min-lines 12 --min-tokens 1 --reporters console): before 17 clones (289 duplicated lines), after 0.Pre-existing bug, not fixed here
_missing_package_messagematchesthere is no package called '([^']+)', but R under a UTF-8 locale prints curly quotes (‘knitrr’). I checked: on main and on this branch, a missing package gets the generic failure report, not the friendly message. Fixing that changes user-visible behavior, so it belongs in its own PR.🤖 Generated with Claude Code