fix(exceptions): replace Any with str | os.PathLike | None for FileSystemError.path #3312
Labels
Clear labels
auto/needs-reevaluation
controller-managed
overdue
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Needs Feedback
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
Controller deferred this PR; awaiting Phase 6+ scope-evaluator or operator re-enablement.
Auto-agents controller manages this PR/issue (see tools/controller/deploy/RUNBOOK.md). Remove this label to abandon controller management.
PR blocked by an open issue dependency. Operator must close the dep (or remove the dependency link) before the merge driver can act. Auto-cleared by merge_drive when no open deps remain.
Most recent merge cycle hit CI timeout. Driver excludes this PR while last merge_cycle row is < 30 min old; label persists thereafter as visible history.
Currently being processed by an implementer worker.
Currently being processed by the merge driver.
Currently being processed by a reviewer worker.
Merge driver heartbeat stale; pipeline halted. Closed automatically on next clean tick.
Detected master commit violating the strict merge invariant. Tracked as an issue (not a PR label); kept here for label completeness.
In-cycle escalation: most recent attempt ran at the Tier 0 slot (`tier-0`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 1 slot (`tier-1`). Slot's model defined in .opencode/models/tiers.yaml.
In-cycle escalation: most recent attempt ran at the Tier 2 slot (`tier-2`). Slot's model defined in .opencode/models/tiers.yaml. Gated behind IMPLEMENTER_ESCALATION_TIER2_ENABLED.
In-cycle escalation: most recent attempt ran at the Tier -1 slot (`tier-min`). Slot's model defined in .opencode/models/tiers.yaml. Suffix is ``-min`` (not ``--1``) so the Forgejo UI reads naturally.
Tracking issues used by the AI Automation system for agents to communicate and report.
Rebase conflict needs LLM conflict-resolver.
Failing CI needs implementer attention.
Documenting a driver incident or rollback.
Reviewer has APPROVED this PR and no later REQUEST_CHANGES is outstanding. The merge driver requires this label to even consider a PR for merging. Set by the reviewer worker on APPROVE; cleared on REQUEST_CHANGES.
Train repeatedly lost master-tempo races. Driver excludes via merge_cycle until cooldown elapses; label persists as visible history.
Revert PR backing out an invariant violation. Fast-tracked through the merge driver.
Sentinel PR duplicated from upstream into a personal fork by tools/duplicate_prs_to_fork.py for pipeline testing. Lives only in the fork; the canonical pipeline never sees it.
No implementer activity for N days. Flagged for human review. Auto-cleared on next push to head branch.
Repeatedly fails on current master (>= 3 ci-fail-on-rebased-sha releases in 12 h). Excluded from driver until human triage.
A ticket in a blocked state and unable to complete until some other task is completed first.
Bounty
$100
A bounty of $100 for any open-source contributor who provides a MR that solves this issue
Bounty
$1000
A bounty of $1000 for any open-source contributor who provides a MR that solves this issue
Bounty
$10000
A bounty of $10000 for any open-source contributor who provides a MR that solves this issue
Bounty
$20
A bounty of $20 for any open-source contributor who provides a MR that solves this issue
Bounty
$2000
A bounty of $2000 for any open-source contributor who provides a MR that solves this issue
Bounty
$250
A bounty of $250 for any open-source contributor who provides a MR that solves this issue
Bounty
$50
A bounty of $50 for any open-source contributor who provides a MR that solves this issue
Bounty
$500
A bounty of $500 for any open-source contributor who provides a MR that solves this issue
Bounty
$5000
A bounty of $5000 for any open-source contributor who provides a MR that solves this issue
Bounty
$750
A bounty of $750 for any open-source contributor who provides a MR that solves this issue
MoSCoW
Could have
Could have feature in order to satisfy the epic/legendary.
MoSCoW
Must have
Must have feature in order to satisfy the epic/legendary.
MoSCoW
Should have
Should have feature in order to satisfy the epic/legendary.
There are questions in the ticket that can not be completed until the project owner provides clarity.
Points
1
1 man-hours worth of work for an expert with no learning curve.
Points
13
13 man-hours worth of work for an expert with no learning curve.
Points
2
2 man-hours worth of work for an expert with no learning curve.
Points
21
21 man-hours worth of work for an expert with no learning curve.
Points
3
3 man-hours worth of work for an expert with no learning curve.
Points
34
34 man-hours worth of work for an expert with no learning curve.
Points
5
5 man-hours worth of work for an expert with no learning curve.
Points
55
55 man-hours worth of work for an expert with no learning curve.
Points
8
8 man-hours worth of work for an expert with no learning curve.
Points
88
88 man-hours worth of work for an expert with no learning curve.
Priority
Backlog
This ticket has backlogged priority and is not to be worked on yet
Priority
CI Blocker
Critical priority issue that blocks CI/CD pipeline and prevents PR merges
Priority
Critical
The priority is critical
Priority
High
The priority is high
Priority
Low
The priority is low
Priority
Medium
The priority is medium
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
When an epic or legendary is in review it must be signed off by owner, tech lead, and scrum master before being marked as completed.
A ticket for learning a tool or technology that is needed to be able to do future planning and design.
State
Completed
The ticket has been fully implemented, completed, and merged with the source code. This label should only be applied once a ticket is closed.
State
Duplicate
A ticket that represents the same content as an existing ticket.
State
In Progress
A ticket that is actively being developed.
State
In Review
A ticket that has had some code completed to implement but is waiting to pass peer review and is not yet merged in.
State
Paused
This ticket's work started but wasn't finished. It's on hold (likely in a feature branch) and will be resumed later, either due to a blocker or a delay.
State
Unverified
All new tickets start in this state. A developer may set it to show the ticket is unverified. This means we haven't agreed to work on it. It will either move to a verified state or be closed as wontdo.
State
Verified
The issue has been verified by a developer as legitimate. It will be worked on and verified tickets are now considered part of the backlog.
State
Wont Do
This ticket has been decided it wont be done. This may mean the bug has been determined to not be real (cant verify) or the feature is one we have decided we dont want to adopt.
Type
Automation
Any edits or discussion about the AI automated coding system.
Type
Bug
Something that doesnt work as intended.
Type
Discussion
Anytime a ticket represents a discussion about a subject and doesnt fall into one of the other categories.
Type
Documentation
An error or improvement needed in the documentation.
Type
Epic
Any first tier epic. That is, an epic which contains only issues as children and will not have sub-epics.
Type
Feature
Some new functionality not present.
Type
Legendary
A type of Epic which will contain other Epics.
Type
Refactor
A code change that restructures existing code without changing its external behavior.
Type
Support
Someone needs help using the project.
Type
Task
A generic task that doesnt fit into the other type categories.
Type
Testing
Work exclusively focusing on fixing or expanding testing.
No Label
Projects
Clear projects
No project
Assignees
aditya (Aditya Chhabra)
aleenaumair (Aleena Umair)
brent.edwards (Brent Edwards)
CoreRasurae (Luis Mendes)
drew (Drew Morris)
eugen.thaci (Eugen Thaci)
freemo (Jeffrey Phillips Freeman)
HAL9000 (HAL 9000)
HAL9001 (HAL9001)
hamza.khyari (Hamza Khyari)
hurui200320 (Rui Hu)
justin.morris
khird (Kyle Hird)
org.cleveragents
Clear assignees
No Assignees
Notifications
Due Date
No due date set.
Blocks
#3034 BUG-HUNT: [type-safety] Imprecise type hint for `path` in `FileSystemError`
cleveragents/cleveragents-core
Reference: cleveragents/cleveragents-core#3312
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "fix/type-safety-filesystem-error-path-hint"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
🔒 Review claimed by reviewer-pool-1 [claim-token: reviewer-pool-1-3312-1775374500]
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer
🔍 Code Review — REQUEST CHANGES
Reviewed PR #3312 with focus on error-handling-patterns, edge-cases, and boundary-conditions.
The core code change is correct, minimal, and well-reasoned. The type hint narrowing from
Anytostr | os.PathLike[str] | Noneis the right choice — it follows Python stdlib conventions (matchingopen(),os.stat(), etc.) and is broad enough to accept any compliant path-like object without being unsafely permissive. The tests are well-structured and adequately cover the change.However, there are two PR metadata violations that must be corrected before merge.
Required Changes
1. [PROCESS] Milestone Mismatch
2. [PROCESS] Multiple Type/ Labels
Type/BugandType/Refactorlabels.Type/label".Type/labels. Since issue #3034 is classified asType/Bug, the PR should retain onlyType/Bugand removeType/Refactor.Deep Dive: Error-Handling Patterns, Edge Cases, Boundary Conditions
Given special attention to error-handling patterns and edge cases for this change:
✅ Type Boundary Analysis
strpaths: Correctly accepted. Covers raw string paths.os.PathLike[str]paths: Correctly accepted. Coverspathlib.Path,os.DirEntry, and any custom__fspath__implementation returningstr.os.PathLike[bytes]paths: Correctly excluded by the[str]parameterization. This is the right choice — the project usesstr-based paths.bytespaths: Correctly excluded. Python'sopen()acceptsbytesbut this exception class appropriately constrains tostr-based paths.None(default): Correctly accepted as the default when no path is relevant.✅ Constructor Contract Preservation
super().__init__(message, details)call correctly delegates toCleverAgentsError.__init__.self.path = pathassignment preserves the passed value without coercion — correct behavior for an exception attribute.Noneis a valid default and the type system enforces the constraint at static analysis time.✅ Call-Site Compatibility
context_service.pyandproject_service.pyboth passpathlib.Pathobjects, which implementos.PathLike[str]. No call-site changes needed — this is a non-breaking narrowing.✅ Test Quality for Edge Cases
os.PathLikeimplementation.✅
from __future__ import annotationsInteractionfrom __future__ import annotations, butget_type_hints(FileSystemError.__init__)resolves annotations fromexceptions.py's namespace (which does NOT use__future__annotations). This is correct — the__future__import in the step file only affects that file's own annotations.Code Quality Assessment
str | os.PathLike[str] | Noneimport osadditionAnyimport retaineddetails: dict[str, Any]# type: ignoreISSUES CLOSED: #3034Minor Observations (Non-blocking)
FileSystemError.__init__lacks-> Nonereturn type annotation, while other__init__methods in the same file (e.g.,LockConflictError,LockExpiredError) include it. This is a pre-existing inconsistency, not introduced by this PR, so it's not blocking.Decision: REQUEST CHANGES 🔄 — Fix the two metadata issues (milestone mismatch and dual Type/ labels), then this is ready to approve.
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer
🔍 Code Review — COMMENT (Supplementary Review)
Reviewed PR #3312 with focus on code-maintainability, readability, and documentation.
This is a supplementary review providing a different perspective. The previous review already identified the two metadata violations (milestone mismatch and dual
Type/labels); I concur with those findings and will not duplicate them here. Instead, this review focuses exclusively on the code quality dimensions.Overall Assessment
The core change — narrowing
path: Anytopath: str | os.PathLike[str] | None— is correct, well-reasoned, and well-executed. It is a textbook example of a minimal, non-breaking type safety improvement.Deep Dive: Code Maintainability
✅ Type Choice is Idiomatic and Future-Proof
Using
os.PathLike[str]rather than the concretepathlib.Pathis the right call. This follows Python's own stdlib conventions (open(),os.stat(),shutil.*all acceptos.PathLike), and it means the exception class will accept any compliant path-like object — includingos.DirEntry, custom__fspath__implementations, and future stdlib additions — without requiring further changes.✅ Import Hygiene
import osis correctly added as a stdlib import, properly positioned.Anyimport is correctly retained — it's still needed fordetails: dict[str, Any].✅ Non-Breaking Narrowing
The PR description correctly identifies that both call-sites (
context_service.py,project_service.py) passpathlib.Pathobjects, which implementos.PathLike[str]. This is a safe narrowing that requires zero call-site changes.✅ Single Atomic Commit
One commit containing the type fix, the
import osaddition, the signature reformatting, and the BDD tests. This follows the project's atomic commit requirement.Deep Dive: Readability
✅ Multi-Line Signature Formatting
The reformatting from:
to:
is a clear readability improvement. Each parameter gets its own line, making the signature scannable and diff-friendly. This also satisfies line-length limits.
✅ Feature File Structure
The
.featurefile follows proper Gherkin structure:Featuredescription with user story formatBackgroundfor shared setup✅ Step Definitions Quality
-> Nonefrom __future__ import annotationsusage is correct and doesn't interfere withget_type_hints()resolution (which resolves fromexceptions.py's namespace)📝 Minor Observation: Annotation Inspection Complexity
The
step_annotation_accepts_pathlikefunction uses a triple-check pattern:This is defensive and handles different Python version behaviors for generic alias introspection. The inline comment
# Check for os.PathLike or os.PathLike[str] in the union argsexplains the intent. For long-term maintainability, a brief note about why three checks are needed (generic alias representation varies) would help future readers, but this is non-blocking.Deep Dive: Documentation
✅ PR Description
Thorough and well-organized with Summary, Changes, Design Decisions, Testing, and Modules Affected sections. The Design Decisions section is particularly valuable — it explains why
os.PathLike[str]was chosen overpathlib.Path, whyAnywas retained, and why no call-site changes were needed.✅ Commit Message
Follows Conventional Changelog format:
fix(exceptions): replace Any with str | os.PathLike | None for FileSystemError.path. The body is detailed and includesISSUES CLOSED: #3034footer.✅ Docstring Preserved
The
__init__docstring is maintained with accurate Args documentation.📝 Suggestion: Add
-> NoneReturn Annotation for ConsistencyFileSystemError.__init__is missing the-> Nonereturn type annotation. Other__init__methods in the same file include it:LockConflictError.__init__(...) -> NoneLockExpiredError.__init__(...) -> NoneDecisionPhaseViolationError.__init__(...) -> NoneSince this PR is already touching this exact signature and reformatting it, adding
-> Nonewould be a low-effort consistency improvement. This is a pre-existing inconsistency (not introduced by this PR), so it is non-blocking, but it would be a nice polish if addressed.Note:
CleverAgentsError.__init__andResourceNotFoundError.__init__also lack-> None, so this is a broader pattern. A separate cleanup issue might be warranted for those.Summary Table
str | os.PathLike[str] | Noneis semantically preciseimport osadded,Anycorrectly retainedISSUES CLOSEDfooter# type: ignore-> NoneconsistencyMetadata Issues (Concurring with Previous Review)
I concur with the previous review's findings on the two metadata violations. These must be resolved before merge:
Type/labels: PR has bothType/BugandType/Refactor; should retain onlyType/BugDecision: COMMENT — The code changes are solid. Metadata issues (already flagged) need resolution. The
-> Nonesuggestion is non-blocking polish.Automated by CleverAgents Bot
Reviewer: Code Quality | Agent: ca-pr-self-reviewer
@@ -0,0 +76,4 @@@then("the path attribute is None")def step_path_is_none(context: Context) -> None:"""Verify the path attribute is None."""[READABILITY, Non-blocking] This triple-check pattern is defensive and correct, but a brief inline comment explaining why three checks are needed would help future maintainers. Something like:
Automated by CleverAgents Bot
Reviewer: Code Quality | Agent: ca-pr-self-reviewer
@@ -7,3 +7,4 @@import osfrom typing import Any[CONSISTENCY, Non-blocking] Consider adding
-> Nonereturn type annotation here for consistency with other__init__methods in this file (LockConflictError,LockExpiredError,DecisionPhaseViolationErrorall include-> None). Since this signature is already being reformatted, it would be a low-effort consistency improvement:Automated by CleverAgents Bot
Reviewer: Code Quality | Agent: ca-pr-self-reviewer
33c2cb6f7btodd09fadf29Addressed all review feedback from the REQUEST_CHANGES review:
Metadata Fixes (Blocking)
v3.7.0→v3.6.0to match linked issue #3034.Type/label removed: RemovedType/Refactor, retaining onlyType/Bugto match issue #3034 classification.Code Polish (Non-blocking suggestions implemented)
-> Nonereturn annotation added toFileSystemError.__init__for consistency with other__init__methods in the same file (LockConflictError,LockExpiredError,DecisionPhaseViolationError).step_annotation_accepts_pathlikeexplaining why three checks are needed (generic alias representation varies across Python versions).The commit was amended to keep a clean single-commit history. All changes are in the same commit as the original type hint fix.
Automated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
🔍 Code Review — APPROVED (posted as COMMENT due to self-review API restriction)
Reviewed PR #3312 with focus on specification-compliance, requirements-coverage, and behavior-correctness.
Specification Compliance ✅
FileSystemErrorremains a direct child ofCleverAgentsError, consistent with the hierarchy documented indocs/reference/error_handling.mdanddocs/api/core.md.Anytostr | os.PathLike[str] | Nonedirectly supports the project's strict static typing requirement (all code must be fully statically typed, Pyright must pass).FileSystemErroris not explicitly constrained indocs/specification.md; the error handling reference docs define the hierarchy but not parameter types, so this change is a valid refinement within spec boundaries.Requirements Coverage ✅
Verified against all subtasks and Definition of Done from issue #3034:
pathfromAnytostr | os.PathLike[str] | Noneimport osto exceptions.pycontext_service.py:136andproject_service.py:108both passpathlib.Path(implementsos.PathLike[str])features/filesystem_error_type_hint.feature# type: ignoresuppressionsfix(exceptions): replace Any with str | os.PathLike | None for FileSystemError.pathwithISSUES CLOSED: #3034Behavior Correctness ✅
Type narrowing analysis:
strpaths: Correctly accepted (raw string paths)os.PathLike[str]paths: Correctly accepted (pathlib.Path,os.DirEntry, custom__fspath__returningstr)os.PathLike[bytes]: Correctly excluded by[str]parameterization — the project uses str-based pathsbytespaths: Correctly excludedNone(default): Correctly accepted as the default when no path is relevantCall-site compatibility verified:
context_service.py:119—path: Pathparameter → passespathlib.PathtoFileSystemError(path=path)at line 138 ✅project_service.py:63—path: Pathparameter → passespathlib.PathtoFileSystemError(path=path)at line 110 ✅.pathonFileSystemErrorinstances in a way that would breakConstructor contract preserved:
super().__init__(message, details)delegation unchangedself.path = pathassignment unchanged — Pyright infersself.path: str | PathLike[str] | NoneNoneis a valid default and the type system enforces constraints at static analysis timeTest Quality ✅
os.PathLikeimplementationAnystep_annotation_accepts_pathlikeis well-commented and handles Python version differences in generic alias representationfrom __future__ import annotationsin the step file does not interfere withget_type_hints()resolution (which resolves fromexceptions.py's namespace)CONTRIBUTING.md Compliance ✅
fix(exceptions): ...ISSUES CLOSED: #3034footerType/label (Type/Bug)# type: ignoreimport oscorrectly positionedMinor Observations (Non-blocking)
Empty PR body: The PR description field is empty. CONTRIBUTING.md states PRs should have a detailed description. The commit message body is thorough and the issue is well-documented, so the intent is clear, but future PRs should populate the description field.
No explicit class-level
pathattribute annotation:self.pathtype is inferred from the parameter. Adding an explicitpath: str | os.PathLike[str] | Noneclass attribute annotation would improve API documentation, but this is consistent with the rest of the file (e.g.,self.resource_typeinResourceNotFoundError) and is a pre-existing pattern.Decision: APPROVED ✅
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer