Move ecosystem-neutral helpers out of Codex-named homes - #465
Conversation
A maintainer review flagged that generic utilities lived in Codex-named modules and were imported cross-package from there: - The safe_* filesystem wrappers (safe_resolve, _safe_stat, safe_is_dir, safe_is_file, safe_exists, safe_is_symlink) move from formats/codex.py to paths.py, joining the pure path predicates extracted there for the same reason. escapes_root moves there too from the codex rule helpers. - safe_display and its private machinery (_redact_userinfo, _CONTROL_CHARS, _MAX_DISPLAY) move to a new core module diagnostics.py; the generic hooks/json_valid.py no longer reaches into rules/builtin/codex/_helpers.py for it. That module now keeps only genuinely Codex-specific helpers. - paths.py gains contained_resolve(path, root), naming the reject-a-symlink-escape idiom; the nine call sites that spelled the exact idiom out are converted, sites with different semantics are deliberately left alone. - tests/test_banned_apis.py ratchets bare Path.resolve() calls: the 29 files containing them today are grandfathered per issue #463, new bare uses fail the test and must use the paths.py wrappers. All import sites updated; no re-export shims left behind. Behavior is unchanged: the full test suite passes and a real-corpus canary run produces an identical violation set before and after. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR centralizes safe path resolution, containment, filesystem predicates, symlink checks, and diagnostic sanitization. Codex, plugin, marketplace, documentation, and agentskill discovery adopt the shared helpers, while tests enforce path-resolution usage and update helper imports. ChangesPath safety and diagnostics
Estimated code review effort: 3 (Moderate) | ~30 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
@codex review |
|
@coderabbitai review |
|
/review |
✅ Action performedReview finished.
|
PR Summary by QodoMove neutral path/diagnostic helpers into core modules
AI Description
Diagram
High-Level Assessment
Files changed (18)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
143 rules✅ Skills:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: faf896248c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| from pathlib import Path | ||
| from typing import Any, Dict, List, Optional | ||
|
|
||
| from skillsaw.paths import contained_resolve, safe_is_dir, safe_is_file, safe_resolve |
There was a problem hiding this comment.
Preserve legacy imports for the moved path helpers
When an existing custom rule imports either previously exposed non-underscore helper (from skillsaw.formats.codex import safe_exists or safe_is_symlink), this import list no longer binds it, so custom-rule loading raises an ImportError and aborts the lint. The repository explicitly preserves legacy import locations for custom rules in tests/test_module_layering.py:9-14; keep compatibility re-exports here while making paths.py canonical.
AGENTS.md reference: AGENTS.md:L88-L89
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Declining: skillsaw.formats.codex does not exist on main or in any release — it was introduced on this unreleased feature branch, so no external code can import from it. Moving the helpers to their intended home before first release is exactly why no deprecation shim is warranted.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/skillsaw/paths.py (1)
66-77: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winState the resolved-root precondition in
contained_resolve’s docstring.
contained_resolveresolvespathbut leavesrootunchanged; callers pre-resolve here, but if the second argument is an unresolved symlinked directory,is_relative_to(root)can incorrectly reject contained paths. Make the docstring explicit thatrootmust already be resolved.🤖 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 `@src/skillsaw/paths.py` around lines 66 - 77, Update the contained_resolve docstring to explicitly state that root must already be resolved before calling the function, while preserving the existing behavior and examples.
🤖 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.
Nitpick comments:
In `@src/skillsaw/paths.py`:
- Around line 66-77: Update the contained_resolve docstring to explicitly state
that root must already be resolved before calling the function, while preserving
the existing behavior and examples.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 238a219f-f64c-473f-8983-51ff7e9fd186
📒 Files selected for processing (18)
src/skillsaw/context.pysrc/skillsaw/diagnostics.pysrc/skillsaw/docs/extractor.pysrc/skillsaw/formats/codex.pysrc/skillsaw/lint_tree.pysrc/skillsaw/paths.pysrc/skillsaw/rules/builtin/agentskills/_helpers.pysrc/skillsaw/rules/builtin/agentskills/unreferenced_files.pysrc/skillsaw/rules/builtin/codex/_helpers.pysrc/skillsaw/rules/builtin/codex/marketplace_json_valid.pysrc/skillsaw/rules/builtin/codex/marketplace_registration.pysrc/skillsaw/rules/builtin/codex/openai_metadata.pysrc/skillsaw/rules/builtin/codex/plugin_json_valid.pysrc/skillsaw/rules/builtin/codex/plugin_structure.pysrc/skillsaw/rules/builtin/hooks/json_valid.pysrc/skillsaw/rules/builtin/marketplace/json_valid.pytests/test_banned_apis.pytests/test_codex_rules.py
Summary
Relocation-only refactor into the
codex-plugin-rulesfeature branch, addressing the review note that ecosystem-neutral utilities lived in Codex-named modules. Behavior is byte-identical.safe_*filesystem wrappers →src/skillsaw/paths.py.safe_resolve,_safe_stat,safe_is_dir,safe_is_file,safe_exists,safe_is_symlinkmove out offormats/codex.py, andescapes_rootmoves out ofrules/builtin/codex/_helpers.py, joining the pure path predicates already extracted there for exactly this reason. All import sites updated (context.py,lint_tree.py,docs/extractor.py, agentskills/marketplace/codex rules, tests); no re-export shims left behind.safe_display→ new core modulesrc/skillsaw/diagnostics.pywith_redact_userinfo,_CONTROL_CHARS,_MAX_DISPLAY. The genericrules/builtin/hooks/json_valid.pyno longer imports it cross-package from the codex rule helpers.rules/builtin/codex/_helpers.pynow keeps only genuinely Codex-specific things (repo-type sets,KEBAB_CASE, strict-JSON helpers,path_problem).contained_resolve(path, root)inpaths.pynames the reject-a-symlink-escape idiom (safe_resolve+is_relative_to, resolution failure →None).contained_resolveconversions (exact-idiom sites only)Converted (9):
formats/codex.pycodex_declared_paths+ both checks incodex_manifest_is_contained;rules/builtin/agentskills/_helpers.pycontained_skill_file;docs/extractor.py_scoped_prose_blocks;context.py_usable,_add(manifest dir), legacy plugin discovery, and both checks in single-skill discovery.Deliberately left alone: boolean-return forms (
lint_tree.py_contained/_contained_in_plugin,context.py_inside), sites that also resolve the root in the same guard (lint_tree.py_add_codex_block/_add_openai_metadata,openai_metadata.py), sites with different failure semantics (context.pyexisting-manifest check,marketplace_registration.pywhere an unresolvable manifest is not a reject), the explicit== rootvariant (context.py_contained), and the mixed-condition walk in_discover_skills_in_dir.Ratchet test
tests/test_banned_apis.pyAST-scanssrc/skillsaw/**/*.pyfor bare.resolve()calls and asserts the offending files are a subset of a frozen 29-file allowlist — existing debt is grandfathered per issue #463, new bare uses are banned, and a stale-entry check makes the ratchet tighten-only. Existing debt is intentionally not paid down here.Verification
make test: 3475 passed (full suite green, no test behavior changes beyond import-path updates)make lintclean;make updateregenerated nothing--format json): 973 violations before, 973 after, violation sets identical, 0 violations fromcodex-*rules in both runsopenshift-eng/ai-helpersrun produces the identical result before and after (its singleplugins-doc-up-to-dateerror is that repo's own docs drift, reproduced byte-for-byte with the base branch)🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests