feat: add issue analyzer script for priority/scope management#1084
feat: add issue analyzer script for priority/scope management#1084
Conversation
WalkthroughAdds an executable CLI script at 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces issue_analyzer.py, a utility script for analyzing and reorganizing GitHub issues for the SynthOrg project. The feedback highlights the need for better error handling when the GitHub CLI is missing and identifies several instances of hardcoded data—such as specific issue numbers and version themes—that make the script brittle. Additionally, a minor optimization for a redundant regular expression was suggested to improve code quality.
scripts/issue_analyzer.py
Outdated
| except subprocess.CalledProcessError as e: | ||
| print(f"Error fetching issues: {e}", file=sys.stderr) | ||
| return [] |
There was a problem hiding this comment.
The current error handling only catches subprocess.CalledProcessError. If the gh CLI is not installed on the system, subprocess.run will raise a FileNotFoundError, which is currently unhandled and will cause the script to crash with a traceback. It is better to catch this specifically and provide a helpful error message.
| except subprocess.CalledProcessError as e: | |
| print(f"Error fetching issues: {e}", file=sys.stderr) | |
| return [] | |
| except FileNotFoundError: | |
| print("Error: 'gh' CLI not found. Please install GitHub CLI.", file=sys.stderr) | |
| return [] | |
| except subprocess.CalledProcessError as e: | |
| print(f"Error fetching issues: {e}", file=sys.stderr) | |
| return [] |
scripts/issue_analyzer.py
Outdated
| if num in [1081, 1082]: | ||
| recommended_prio = "critical" | ||
| recommended_scope = "medium" | ||
| reason = "Blocking core functionality - dashboard editing doesn't work" | ||
|
|
||
| # HIGH: Quick wins with major value | ||
| elif num in [1080, 1079, 1077]: | ||
| recommended_prio = "high" | ||
| recommended_scope = "small" | ||
| reason = "Fast implementation, high user value" | ||
|
|
||
| # Research items - deferrable | ||
| elif issue_type == "research": | ||
| recommended_prio = "low" | ||
| reason = "Needs evaluation before implementation" | ||
|
|
||
| # Speculative features - future | ||
| elif num in [251, 252, 253, 254, 255, 242, 241, 250, 249, 248, 246, 1012, 1005]: | ||
| recommended_prio = "low" | ||
| reason = "Aspirational features - revisit later" |
There was a problem hiding this comment.
The logic for re-assessing priority and scope relies on several blocks of hardcoded issue numbers. This makes the script very brittle and specific to the current state of the repository. As issues are closed or new ones are created, this logic will quickly become outdated. Consider using GitHub labels (e.g., impact:critical, effort:medium) or metadata within the issue body to drive these recommendations dynamically.
scripts/issue_analyzer.py
Outdated
| # Common dependency patterns | ||
| patterns = [ | ||
| r"(?:depends?\s+(?:on|upon)|blocked\s+(?:by|on)|requires?|needs?|builds?\s+on)\s*[:\s]*#(\d+)", | ||
| r"(?:follow(?:s|ing|[- ]up)|extends?|extends?)\s*[:\s]*#(\d+)", |
There was a problem hiding this comment.
| v064 = [] # Critical | ||
| v065 = [] # High | ||
| v070 = [] # Medium | ||
| v080 = [] # Low + research | ||
|
|
||
| for c in categorized: | ||
| if c["recommended_prio"] == "critical": | ||
| v064.append(c) | ||
| elif c["recommended_prio"] == "high": | ||
| v065.append(c) | ||
| elif c["recommended_prio"] == "medium": | ||
| v070.append(c) | ||
| else: | ||
| v080.append(c) | ||
|
|
||
| print("\n### v0.6.4 (CRITICAL - 2 issues)") | ||
| print("Theme: Foundation - dashboard editing actually works\n") | ||
| for c in sorted(v064, key=lambda x: x["num"]): | ||
| print(f" #{c['num']}: {c['title'][:55]} [{c['recommended_scope']}]") |
There was a problem hiding this comment.
The version buckets and associated display logic (such as the hardcoded '2 issues' in the print statement) are tightly coupled to the current project state. This limits the tool's longevity. Consider abstracting the versioning and theme logic into a configuration object or deriving it from repository milestones to ensure the tool remains useful as the project progresses.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
Adds a standalone Python CLI (scripts/issue_analyzer.py) that uses the GitHub CLI (gh) to fetch open issues and produce analyses/summaries focused on priority, scope, versions, and dependency chains.
Changes:
- Introduces
scripts/issue_analyzer.pywith commands:analyze,summary,critical,propose,deps. - Implements heuristics to infer scope/priority adjustments from labels and issue body content.
- Adds dependency-chain detection via regex patterns and prints proposed version bucket groupings.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
scripts/issue_analyzer.py
Outdated
| } | ||
|
|
||
| for c in categorized: | ||
| groups.get(c["recommended_prio"], []).append(c) | ||
|
|
||
| # Print groups | ||
| for prio in ["critical", "high", "medium", "low"]: |
There was a problem hiding this comment.
In print_full_analysis, issues whose recommended_prio isn't one of the predefined keys are silently dropped because groups.get(..., []) returns a temporary list that isn't stored back into groups. This can happen when issues are missing a prio: label and get_label_value() returns "unknown". Consider adding an explicit "unscoped"/"unknown" bucket (or normalizing unknown priorities to a default) so every issue appears in the output.
| } | |
| for c in categorized: | |
| groups.get(c["recommended_prio"], []).append(c) | |
| # Print groups | |
| for prio in ["critical", "high", "medium", "low"]: | |
| "unknown": [], | |
| } | |
| for c in categorized: | |
| prio = c["recommended_prio"] | |
| if prio not in groups: | |
| prio = "unknown" | |
| groups[prio].append(c) | |
| # Print groups | |
| for prio in ["critical", "high", "medium", "low", "unknown"]: |
scripts/issue_analyzer.py
Outdated
| print("\n### v0.6.4 (CRITICAL - 2 issues)") | ||
| print("Theme: Foundation - dashboard editing actually works\n") | ||
| for c in sorted(v064, key=lambda x: x["num"]): | ||
| print(f" #{c['num']}: {c['title'][:55]} [{c['recommended_scope']}]") | ||
|
|
There was a problem hiding this comment.
The heading "### v0.6.4 (CRITICAL - 2 issues)" is hardcoded to 2 issues, but the actual critical issue count is computed dynamically into v064. If the set of critical issues changes, this output becomes incorrect. Consider formatting the header with len(v064) instead of a fixed number.
scripts/issue_analyzer.py
Outdated
| def categorize_issue(issue: dict) -> dict: | ||
| """Categorize an issue by priority and scope.""" | ||
| num = issue["number"] | ||
| title = issue["title"] | ||
| labels = issue.get("labels", []) |
There was a problem hiding this comment.
There are existing unit tests for scripts under tests/unit/scripts/ (e.g. test_generate_comparison.py). Consider adding tests around categorize_issue() (and related analyze_scope() helpers) to lock in the categorization heuristics and catch regressions like missing/unknown prio: labels affecting output.
scripts/issue_analyzer.py
Outdated
| reason += "; No complex logic or acceptance criteria - likely medium" | ||
| elif ( | ||
| analysis["has_endpoints"] | ||
| and analysis["ac_count"] < ACCEPTANCE_CRITERIA_THRESHOLD | ||
| ): | ||
| recommended_scope = "medium" | ||
| reason += "; Standard CRUD endpoints, not complex architecture" |
There was a problem hiding this comment.
reason can end up starting with a leading "; " when only a scope reassessment applies (since reason starts empty and then you append "; ..."). This produces awkward output like Reason: ; Standard CRUD endpoints.... Consider adding the separator only when reason is already non-empty.
| reason += "; No complex logic or acceptance criteria - likely medium" | |
| elif ( | |
| analysis["has_endpoints"] | |
| and analysis["ac_count"] < ACCEPTANCE_CRITERIA_THRESHOLD | |
| ): | |
| recommended_scope = "medium" | |
| reason += "; Standard CRUD endpoints, not complex architecture" | |
| reason = ( | |
| f"{reason}; No complex logic or acceptance criteria - likely medium" | |
| if reason | |
| else "No complex logic or acceptance criteria - likely medium" | |
| ) | |
| elif ( | |
| analysis["has_endpoints"] | |
| and analysis["ac_count"] < ACCEPTANCE_CRITERIA_THRESHOLD | |
| ): | |
| recommended_scope = "medium" | |
| reason = ( | |
| f"{reason}; Standard CRUD endpoints, not complex architecture" | |
| if reason | |
| else "Standard CRUD endpoints, not complex architecture" | |
| ) |
scripts/issue_analyzer.py
Outdated
| version_counts = defaultdict( | ||
| lambda: {"total": 0, "high": 0, "medium": 0, "low": 0, "unscoped": 0} | ||
| ) |
There was a problem hiding this comment.
print_summary() tracks an unscoped count per version (see the version_counts initializer), but the printed table only includes Total/High/Medium/Low. This can make Total not equal High+Medium+Low, which is confusing. Consider printing an Unscoped column or folding those counts into an existing bucket.
scripts/issue_analyzer.py
Outdated
| def main(): | ||
| """Main entry point for the issue analyzer.""" | ||
| if len(sys.argv) < MIN_CLI_ARGS: | ||
| print(__doc__) | ||
| sys.exit(1) |
There was a problem hiding this comment.
This script's entrypoint diverges from the pattern used by other scripts/*.py utilities (e.g. scripts/export_openapi.py), where main() returns an int and if __name__ == "__main__": sys.exit(main()). Consider returning explicit exit codes (0/1) and exiting via sys.exit(main()) for consistent CLI behavior and easier testing.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/issue_analyzer.py`:
- Around line 352-353: The hard-coded "2 issues" string in the print statements
should be replaced with a computed count from the actual issue list for that
release (e.g. use len(issues_for_version) or len(version_issues) instead of the
literal 2) and format the output with correct pluralization ("1 issue" vs "N
issues"); update the print calls (the lines that currently call print("###
v0.6.4 (CRITICAL - 2 issues)") and the following theme print) to compute count =
len(<the list holding v0.6.4 issues>) and interpolate that count into the header
string, falling back to zero if the list is missing or None.
- Around line 381-401: Validate the CLI command before calling
run_gh_issue_list(): check the value of the local variable command (from
sys.argv[1]) against the allowed commands used later (print_full_analysis,
print_summary, print_critical, find_dependencies, propose_reorganization) and
exit with the usage message if it's not one of them; only after the command is
known to be valid, call run_gh_issue_list() and proceed to dispatch to the
appropriate function (analyze -> print_full_analysis, summary -> print_summary,
critical -> print_critical, deps -> find_dependencies, propose ->
propose_reorganization).
- Around line 208-217: The current loop silently drops entries whose
c["recommended_prio"] isn't a key in groups; update the logic around the groups
dict and the loop over categorized (variable names: groups, categorized, and
c["recommended_prio"]) to ensure unknown priorities are preserved by either
adding an "unknown" bucket up-front or using
groups.setdefault(c["recommended_prio"], []).append(c) (or an explicit
membership check and creation) so items with unexpected priorities are collected
instead of being appended to a throwaway list.
- Around line 196-197: Several public functions lack return type annotations and
Google-style docstrings; add explicit return type hints to the signatures and
insert Google-style docstrings (with Args, Returns, and Raises where applicable)
immediately under each def for: run_gh_issue_list, get_label_value,
analyze_scope, categorize_issue, print_full_analysis, print_summary,
print_critical, find_dependencies, propose_reorganization, and main; ensure the
documented parameter types and return types match the function signatures so
mypy strict mode and ruff D rules pass.
- Around line 42-57: The try/except block that calls subprocess.run(cmd, ...)
should be changed to add a timeout (e.g., timeout=30) to the subprocess.run call
and stop swallowing failures: remove the broad except handlers that print and
return [] and instead let subprocess.CalledProcessError and json.JSONDecodeError
propagate (or re-raise a wrapped exception) so callers can distinguish fetch
errors from "no issues"; also perform command validation before executing
subprocess.run by validating the variable cmd earlier so invalid commands don't
trigger the fetch. Reference the existing subprocess.run(...) call, the local
cmd variable, and the current except handlers for subprocess.CalledProcessError
and json.JSONDecodeError when making these changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 109fc893-a65f-4a26-8dc0-a68f94558048
📒 Files selected for processing (1)
scripts/issue_analyzer.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Agent
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotationsin Python files -- Python 3.14 has PEP 649 native lazy annotations.
Use PEP 758 except syntax: useexcept A, B:(no parentheses) in Python 3.14+ code -- ruff enforces this.
All public functions in Python must have type hints and docstrings in Google style. Enforce with mypy strict mode and ruff D rules.
UseNotBlankStr(fromcore.types) for all identifier/name fields in Pydantic models -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time.
Use@computed_fieldfor derived values in Pydantic models instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens).
Use frozen Pydantic models for config/identity. Use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task.
Python line length: 88 characters (ruff).
Python functions must be < 50 lines, files < 800 lines.
Handle errors explicitly in Python code, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files) in Python code.
All provider calls in Python code must go through...
Files:
scripts/issue_analyzer.py
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Version bumping (pre-1.0): `fix:` = patch, `feat:` = patch, `feat!:` = minor, `BREAKING CHANGE` trailer = minor. Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T11:08:01.542Z
Learning: GitHub issue queries: use `gh issue list` via Bash (not MCP tools) — MCP `list_issues` has unreliable field data.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Use `gh issue list` via Bash (not MCP tools) for GitHub issue queries -- MCP `list_issues` has unreliable field data.
- Add return type hints to all functions (None, list[tuple[int,int]]) - Add Google-style docstrings to all public functions (Args sections) - Validate command BEFORE fetching issues (fail fast with set lookup) - Move MIN_CLI_ARGS to top-level constant
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
scripts/issue_analyzer.py (1)
445-446:⚠️ Potential issue | 🟡 Minor
main()still needs a Google-style docstring.
main()has a one-line docstring, but this repository requires Google-style sections for public functions.📝 Proposed fix
def main() -> None: - """Main entry point for the issue analyzer.""" + """Run the issue analyzer CLI command dispatcher. + + Returns: + None + + Raises: + SystemExit: If arguments are invalid or command execution fails. + """As per coding guidelines: "All public functions in Python must have type hints and docstrings in Google style."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/issue_analyzer.py` around lines 445 - 446, The one-line docstring for the public function main() needs to be replaced with a Google-style docstring: expand the docstring of main() to include a short summary line, a blank line, an Args section (if there are parameters; if none, omit or state none), and a Returns section describing the return type (None) and side effects, and any Raises section if exceptions are propagated; keep the existing type hint "-> None" and update the docstring in the def main() block to follow that structure so it complies with the repository's Google-style docstring requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/issue_analyzer.py`:
- Around line 291-315: The priority-check logic currently only recognizes
["high","medium","low"], so labels like "prio:critical" get counted as
"unscoped"; update the code around version_counts and the prio handling (the
defaultdict setup and the two places where prio is checked) to include a
"critical" bucket and to treat "critical" as a valid priority (e.g., add
"critical" to the default dict keys and to the list used in the conditional that
uses get_label_value(labels, "prio")), and make the same change in the mirrored
block referenced at lines ~320-327; refer to variables/function names
version_counts, get_label_value, ZAP_REPORT_ISSUE_NUMBER, and issues to find the
relevant logic.
- Around line 132-206: categorize_issue is too long and mixes label extraction,
priority rules, and scope reassessment; split it into smaller helpers: create
extract_issue_meta(issue) to pull
num/title/labels/body/current_prio/current_scope/issue_type/versions,
determine_priority(num, current_prio, issue_type, versions) to encapsulate the
hardcoded priority rules (handle CRITICAL/HIGH/research/speculative lists), and
reassess_scope(current_scope, analysis) to encapsulate the "Scope
re-assessments" block (use ACCEPTANCE_CRITERIA_THRESHOLD and return updated
scope + scope reason); then have categorize_issue call extract_issue_meta ->
analyze_scope -> determine_priority -> reassess_scope, merge reasons and
analysis, and return the same dict shape as before (preserve keys
"recommended_prio", "recommended_scope", "reason", and **analysis).
- Around line 392-443: propose_reorganization currently sorts issues only by
recommended_prio and simple keys (functions/vars: propose_reorganization,
categorized, v064/v065/v070/v080) and can place blocked items before their
prerequisites; call find_dependencies for the incoming issues to build a
dependency map and, for each version bucket, partition items into unblocked (no
unmet dependencies in other buckets) and blocked sets, sort unblocked items
first (then blocked), and use that ordering in the prints (replace the current
sorted(...) iterations for v064/v065/v070/v080 so unblocked issues appear before
blocked ones while preserving existing secondary sort keys like num or has_ui).
Ensure find_dependencies usage identifies cross-issue prerequisites and treats
ZAP_REPORT_ISSUE_NUMBER the same as elsewhere.
- Around line 54-76: The try/except around subprocess.run that invokes cmd with
timeout=30 must also catch subprocess.TimeoutExpired to avoid an unformatted
traceback; add an except subprocess.TimeoutExpired as e block after the
FileNotFoundError handler that prints a clear error to stderr (including the cmd
and timeout or e) and exits with sys.exit(1), referencing the existing
subprocess.run call, the cmd variable, and the timeout value to help locate the
code.
---
Duplicate comments:
In `@scripts/issue_analyzer.py`:
- Around line 445-446: The one-line docstring for the public function main()
needs to be replaced with a Google-style docstring: expand the docstring of
main() to include a short summary line, a blank line, an Args section (if there
are parameters; if none, omit or state none), and a Returns section describing
the return type (None) and side effects, and any Raises section if exceptions
are propagated; keep the existing type hint "-> None" and update the docstring
in the def main() block to follow that structure so it complies with the
repository's Google-style docstring requirement.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: ff9686e7-b163-448a-831a-4cc131f6a0d0
📒 Files selected for processing (1)
scripts/issue_analyzer.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotationsin Python files -- Python 3.14 has PEP 649 native lazy annotations.
Use PEP 758 except syntax: useexcept A, B:(no parentheses) in Python 3.14+ code -- ruff enforces this.
All public functions in Python must have type hints and docstrings in Google style. Enforce with mypy strict mode and ruff D rules.
UseNotBlankStr(fromcore.types) for all identifier/name fields in Pydantic models -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time.
Use@computed_fieldfor derived values in Pydantic models instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens).
Use frozen Pydantic models for config/identity. Use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task.
Python line length: 88 characters (ruff).
Python functions must be < 50 lines, files < 800 lines.
Handle errors explicitly in Python code, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files) in Python code.
All provider calls in Python code must go through...
Files:
scripts/issue_analyzer.py
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Version bumping (pre-1.0): `fix:` = patch, `feat:` = patch, `feat!:` = minor, `BREAKING CHANGE` trailer = minor. Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to {pyproject.toml,src/synthorg/__init__.py} : Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: When review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes), fix them all in Python and other code. No deferring, no 'out of scope' skipping.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-01T05:46:17.064Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T05:46:17.064Z
Learning: Fix all valid issues found by review agents (including pre-existing issues in surrounding code, suggestions, and adjacent findings) — never skip or defer
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Prioritize issues by dependency order, not priority labels — unblocked dependencies come first.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Prioritize issues by dependency order, not priority labels -- unblocked dependencies come first.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Export OpenAPI schema with `uv run python scripts/export_openapi.py` (required before docs build)
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-30T16:36:33.512Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: cli/CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:33.512Z
Learning: Applies to cli/cmd/**/*.go : CLI exit codes must follow the standard: 0 = Success, 1 = Runtime error, 2 = Usage error (bad arguments), 3 = Unhealthy (backend/containers), 4 = Unreachable (Docker not available), 10 = Updates available (`--check`)
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Export OpenAPI schema with: `uv run python scripts/export_openapi.py` (needed before docs build).
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Use `gh issue list` via Bash (not MCP tools) — MCP `list_issues` has unreliable field data
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Applies to **/*.py : All public functions in Python must have type hints and docstrings in Google style. Enforce with mypy strict mode and ruff D rules.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-03T07:50:58.367Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T07:50:58.367Z
Learning: Applies to **/*.py : Use Google-style docstrings, required on all public classes and functions (enforced by ruff D rules)
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-15T22:16:31.632Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T22:16:31.632Z
Learning: Applies to **/*.py : Type hints: all public functions and classes require type hints, enforced with mypy strict mode.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-16T10:40:25.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T10:40:25.144Z
Learning: Applies to **/*.py : Docstrings: Google style, required on public classes and functions (enforced by ruff D rules).
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-02T08:47:46.313Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
Learning: Applies to **/*.py : Include type hints for all public functions; enforce with mypy strict mode
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-15T21:49:53.264Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:49:53.264Z
Learning: Fix everything valid — never skip when review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes). No deferring, no 'out of scope' skipping.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-21T11:08:01.542Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T11:08:01.542Z
Learning: GitHub issue queries: use `gh issue list` via Bash (not MCP tools) — MCP `list_issues` has unreliable field data.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Use `gh issue list` via Bash (not MCP tools) for GitHub issue queries -- MCP `list_issues` has unreliable field data.
Applied to files:
scripts/issue_analyzer.py
| def propose_reorganization(issues: list[dict]) -> None: | ||
| """Propose new version organization based on priority analysis. | ||
|
|
||
| Args: | ||
| issues: List of issue dictionaries from GitHub API. | ||
| """ | ||
| categorized = [ | ||
| categorize_issue(i) for i in issues if i["number"] != ZAP_REPORT_ISSUE_NUMBER | ||
| ] | ||
|
|
||
| print("\n" + "=" * 60) | ||
| print("PROPOSED REORGANIZATION") | ||
| print("=" * 60) | ||
|
|
||
| # Define version buckets based on recommended priority | ||
| v064 = [] # Critical | ||
| v065 = [] # High | ||
| v070 = [] # Medium | ||
| v080 = [] # Low + research | ||
|
|
||
| for c in categorized: | ||
| if c["recommended_prio"] == "critical": | ||
| v064.append(c) | ||
| elif c["recommended_prio"] == "high": | ||
| v065.append(c) | ||
| elif c["recommended_prio"] == "medium": | ||
| v070.append(c) | ||
| else: | ||
| v080.append(c) | ||
|
|
||
| print(f"\n### v0.6.4 (CRITICAL - {len(v064)} issue{'s' if len(v064) != 1 else ''})") | ||
| print("Theme: Foundation - dashboard editing actually works\n") | ||
| for c in sorted(v064, key=lambda x: x["num"]): | ||
| print(f" #{c['num']}: {c['title'][:55]} [{c['recommended_scope']}]") | ||
|
|
||
| print(f"\n### v0.6.5 (HIGH PRIORITY - {len(v065)} issues)") | ||
| print("Theme: User value - polish and features\n") | ||
| for c in sorted(v065, key=lambda x: (x["has_ui"], x["num"]), reverse=True)[ | ||
| :MAX_DISPLAY_ISSUES | ||
| ]: | ||
| print(f" #{c['num']}: {c['title'][:55]} [{c['recommended_scope']}]") | ||
| if len(v065) > MAX_DISPLAY_ISSUES: | ||
| print(f" ... and {len(v065) - MAX_DISPLAY_ISSUES} more") | ||
|
|
||
| print(f"\n### v0.7.0 (MEDIUM - {len(v070)} issues)") | ||
| print("Theme: Engine hardening\n") | ||
| for c in sorted(v070, key=lambda x: x["num"])[:8]: | ||
| print(f" #{c['num']}: {c['title'][:55]} [{c['recommended_scope']}]") | ||
|
|
||
| print(f"\n### v0.8.x / Future ({len(v080)} issues)") | ||
| print("Theme: Research and aspirational features") | ||
|
|
There was a problem hiding this comment.
Reorganization should account for dependency order, not only priority buckets.
Current release bucketing ignores dependency edges, so blocked issues can be scheduled before their prerequisites. Use find_dependencies() output to order unblocked items first within each proposed release.
Based on learnings: "Prioritize issues by dependency order, not priority labels -- unblocked dependencies come first."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/issue_analyzer.py` around lines 392 - 443, propose_reorganization
currently sorts issues only by recommended_prio and simple keys (functions/vars:
propose_reorganization, categorized, v064/v065/v070/v080) and can place blocked
items before their prerequisites; call find_dependencies for the incoming issues
to build a dependency map and, for each version bucket, partition items into
unblocked (no unmet dependencies in other buckets) and blocked sets, sort
unblocked items first (then blocked), and use that ordering in the prints
(replace the current sorted(...) iterations for v064/v065/v070/v080 so unblocked
issues appear before blocked ones while preserving existing secondary sort keys
like num or has_ui). Ensure find_dependencies usage identifies cross-issue
prerequisites and treats ZAP_REPORT_ISSUE_NUMBER the same as elsewhere.
- Extract hardcoded issue numbers to top-level constants (CRITICAL_ISSUES, HIGH_QUICK_WIN_ISSUES, DEFERRED_ISSUES) for maintainability - Add 'Other' column to print_summary() table so Total equals the sum of all printed columns (previously unscoped issues were counted but hidden)
- Catch subprocess.TimeoutExpired for controlled error on gh CLI timeout - Split categorize_issue into _determine_priority and _reassess_scope helpers to stay under 50-line function limit - Add critical priority bucket to print_summary so prio:critical labels are not miscounted as unscoped
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/issue_analyzer.py`:
- Around line 410-445: The function find_dependencies currently mixes
computation and printing; remove the printing side effects from
find_dependencies so it only returns the list of (source, target) tuples, then
create a new function print_dependencies(deps: list[tuple[int,int]]) that
contains the existing console output (header, formatted lines, and "No explicit
dependencies found" message); update main() to call deps =
find_dependencies(issues) and then call print_dependencies(deps) so the logic is
pure and printing is separated (keep the same return type for find_dependencies
and keep existing patterns and variable names such as find_dependencies,
print_dependencies, deps, and main).
- Around line 126-127: The code assigns `_ = title` but never uses the title, so
remove the unused `title` parameter from the function signature (and delete the
`_ = title` line) and update all call sites to stop passing a title; also update
the function's docstring/type hints and any tests accordingly. If you prefer to
keep it for planned work, replace `_ = title` with a clear TODO comment like
"TODO: use title in future issue analysis" and keep a FIXME/TODO marker in the
function's docstring or above the line so intent is explicit.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: ff09802f-6d85-4154-95c8-fec0003d9ae8
📒 Files selected for processing (1)
scripts/issue_analyzer.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotationsin Python files -- Python 3.14 has PEP 649 native lazy annotations.
Use PEP 758 except syntax: useexcept A, B:(no parentheses) in Python 3.14+ code -- ruff enforces this.
All public functions in Python must have type hints and docstrings in Google style. Enforce with mypy strict mode and ruff D rules.
UseNotBlankStr(fromcore.types) for all identifier/name fields in Pydantic models -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time.
Use@computed_fieldfor derived values in Pydantic models instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens).
Use frozen Pydantic models for config/identity. Use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task.
Python line length: 88 characters (ruff).
Python functions must be < 50 lines, files < 800 lines.
Handle errors explicitly in Python code, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files) in Python code.
All provider calls in Python code must go through...
Files:
scripts/issue_analyzer.py
🧠 Learnings (23)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Version bumping (pre-1.0): `fix:` = patch, `feat:` = patch, `feat!:` = minor, `BREAKING CHANGE` trailer = minor. Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Use `gh issue list` via Bash (not MCP tools) — MCP `list_issues` has unreliable field data
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T11:08:01.542Z
Learning: GitHub issue queries: use `gh issue list` via Bash (not MCP tools) — MCP `list_issues` has unreliable field data.
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: When review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes), fix them all in Python and other code. No deferring, no 'out of scope' skipping.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-01T05:46:17.064Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T05:46:17.064Z
Learning: Fix all valid issues found by review agents (including pre-existing issues in surrounding code, suggestions, and adjacent findings) — never skip or defer
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Prioritize issues by dependency order, not priority labels — unblocked dependencies come first.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Prioritize issues by dependency order, not priority labels -- unblocked dependencies come first.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Export OpenAPI schema with `uv run python scripts/export_openapi.py` (required before docs build)
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-30T16:36:33.512Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: cli/CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:33.512Z
Learning: Applies to cli/cmd/**/*.go : CLI exit codes must follow the standard: 0 = Success, 1 = Runtime error, 2 = Usage error (bad arguments), 3 = Unhealthy (backend/containers), 4 = Unreachable (Docker not available), 10 = Updates available (`--check`)
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Export OpenAPI schema with: `uv run python scripts/export_openapi.py` (needed before docs build).
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Use `gh issue list` via Bash (not MCP tools) — MCP `list_issues` has unreliable field data
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Applies to **/*.py : All public functions in Python must have type hints and docstrings in Google style. Enforce with mypy strict mode and ruff D rules.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-03T07:50:58.367Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-03T07:50:58.367Z
Learning: Applies to **/*.py : Use Google-style docstrings, required on all public classes and functions (enforced by ruff D rules)
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-15T22:16:31.632Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T22:16:31.632Z
Learning: Applies to **/*.py : Type hints: all public functions and classes require type hints, enforced with mypy strict mode.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-16T10:40:25.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T10:40:25.144Z
Learning: Applies to **/*.py : Docstrings: Google style, required on public classes and functions (enforced by ruff D rules).
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-02T08:47:46.313Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
Learning: Applies to **/*.py : Include type hints for all public functions; enforce with mypy strict mode
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-15T21:49:53.264Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:49:53.264Z
Learning: Fix everything valid — never skip when review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes). No deferring, no 'out of scope' skipping.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-21T11:08:01.542Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T11:08:01.542Z
Learning: GitHub issue queries: use `gh issue list` via Bash (not MCP tools) — MCP `list_issues` has unreliable field data.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Use `gh issue list` via Bash (not MCP tools) for GitHub issue queries -- MCP `list_issues` has unreliable field data.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Python tests have a 30 second timeout per test (global in `pyproject.toml` -- do not add per-file `pytest.mark.timeout(30)` markers; non-default overrides like `timeout(60)` are allowed).
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Functions must be less than 50 lines; files must be less than 800 lines
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-01T06:12:03.047Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T06:12:03.047Z
Learning: Applies to {**/*.py,web/src/**/*.{ts,tsx}} : Keep functions under 50 lines and files under 800 lines
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-04-05T22:36:24.545Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T22:36:24.545Z
Learning: Applies to **/*.py : Python functions must be < 50 lines, files < 800 lines.
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-28T09:24:37.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T09:24:37.044Z
Learning: Applies to **/*.{py,ts,tsx,go} : Functions should be under 50 lines; files should be under 800 lines
Applied to files:
scripts/issue_analyzer.py
📚 Learning: 2026-03-17T13:48:18.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T13:48:18.114Z
Learning: Applies to **/*.py : Functions: < 50 lines, files < 800 lines.
Applied to files:
scripts/issue_analyzer.py
🔇 Additional comments (10)
scripts/issue_analyzer.py (10)
479-494: Reorganization still ignores dependency order.The sorting logic (e.g.,
sorted(v064, key=lambda x: x["num"])) organizes issues without considering dependency edges fromfind_dependencies(). Blocked issues could be scheduled before their prerequisites.Based on learnings: "Prioritize issues by dependency order, not priority labels -- unblocked dependencies come first."
21-42: Well-structured constants for issue overrides.The extraction of hardcoded issue lists into typed top-level constants (
CRITICAL_ISSUES,HIGH_QUICK_WIN_ISSUES,DEFERRED_ISSUES) improves maintainability and makes the priority override logic explicit.
68-96: Robust error handling with timeout.All exception paths are now properly handled:
TimeoutExpiredwith clear timeout value in messageFileNotFoundErrorwith helpful installation linkCalledProcessErrorandJSONDecodeErrorwith stderr outputThe 30-second timeout prevents indefinite hangs on network issues.
99-114: Clean label extraction helper.Simple, focused function with proper type hints and docstring.
152-211: Good refactoring of categorization logic.The extraction of
_determine_priorityand_reassess_scopefrom the originalcategorize_issueaddresses the past review feedback about function size. Each helper has a single responsibility with clear inputs/outputs.
213-252: Properly refactored orchestration function.The
categorize_issuefunction is now well under 50 lines and delegates priority determination and scope reassessment to dedicated helpers. The return typedict[str, str | int | list[str]]accurately reflects the mixed payload.
302-314: Unknown priorities now properly bucketed.The addition of the
"unknown"bucket (line 307) and the explicit fallback logic (lines 312-314) ensures issues with unexpected priority values are collected rather than silently dropped.
336-346: Summary buckets now include critical and other.The
known_priosset includes"critical"and the default dict includes an"other"bucket, addressing the past review feedback about miscounting and missing columns. Totals now equal the sum of displayed buckets.
477-478: Dynamic issue count with proper pluralization.The header now uses
{len(v064)}with conditional plural suffix, addressing the past review feedback about the hard-coded "2 issues" value.
506-517: Command validation before network call.The command is validated against
valid_commands(lines 509-513) before callingrun_gh_issue_list()(line 517), addressing the past review feedback about unnecessary network requests on invalid commands.
| body_lower = body.lower() | ||
| _ = title # title may be used for future analysis |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider removing unused title parameter.
The title parameter is explicitly marked unused (_ = title) with a comment about "future analysis use." If there's no concrete plan to use it soon, removing it simplifies the API and avoids dead code. Otherwise, a TODO comment would make the intention clearer.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/issue_analyzer.py` around lines 126 - 127, The code assigns `_ =
title` but never uses the title, so remove the unused `title` parameter from the
function signature (and delete the `_ = title` line) and update all call sites
to stop passing a title; also update the function's docstring/type hints and any
tests accordingly. If you prefer to keep it for planned work, replace `_ =
title` with a clear TODO comment like "TODO: use title in future issue analysis"
and keep a FIXME/TODO marker in the function's docstring or above the line so
intent is explicit.
| def find_dependencies(issues: list[dict]) -> list[tuple[int, int]]: | ||
| """Find dependency chains in issue bodies. | ||
|
|
||
| Args: | ||
| issues: List of issue dictionaries from GitHub API. | ||
|
|
||
| Returns: | ||
| List of (source_issue, target_issue) dependency tuples. | ||
| """ | ||
| deps = [] | ||
|
|
||
| for issue in issues: | ||
| body = issue.get("body", "") or "" | ||
|
|
||
| # Common dependency patterns | ||
| patterns = [ | ||
| r"(?:depends?\s+(?:on|upon)|blocked\s+(?:by|on)|requires?|needs?|builds?\s+on)\s*[:\s]*#(\d+)", | ||
| r"(?:follow(?:s|ing|[- ]up)|extends?)\s*[:\s]*#(\d+)", | ||
| ] | ||
|
|
||
| for pattern in patterns: | ||
| matches = re.findall(pattern, body, re.IGNORECASE) | ||
| deps.extend((issue["number"], int(m)) for m in matches) | ||
|
|
||
| print("\n" + "=" * 60) | ||
| print("DEPENDENCY CHAINS") | ||
| print("=" * 60) | ||
|
|
||
| if deps: | ||
| for src, dst in sorted(set(deps)): | ||
| print(f" #{src} -> depends on -> #{dst}") | ||
| else: | ||
| print(" No explicit dependencies found") | ||
|
|
||
| return deps | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Function mixes computation with side effects.
find_dependencies both prints output and returns the dependency list, but the return value is unused in main(). For better separation of concerns, consider either:
- Rename to
print_dependenciesand remove the return, or - Extract printing into a separate function and have
find_dependenciesbe pure
This is a minor design smell for a CLI script but would improve testability.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/issue_analyzer.py` around lines 410 - 445, The function
find_dependencies currently mixes computation and printing; remove the printing
side effects from find_dependencies so it only returns the list of (source,
target) tuples, then create a new function print_dependencies(deps:
list[tuple[int,int]]) that contains the existing console output (header,
formatted lines, and "No explicit dependencies found" message); update main() to
call deps = find_dependencies(issues) and then call print_dependencies(deps) so
the logic is pure and printing is separated (keep the same return type for
find_dependencies and keep existing patterns and variable names such as
find_dependencies, print_dependencies, deps, and main).
🤖 I have created a release *beep* *boop* --- ## [0.6.2](v0.6.1...v0.6.2) (2026-04-06) ### Features * add issue analyzer script for priority/scope management ([#1084](#1084)) ([1ccba27](1ccba27)) * config fixes and deferred improvements from PR [#1058](#1058) review ([#1067](#1067)) ([2cac2d3](2cac2d3)), closes [#1061](#1061) [#1060](#1060) * cumulative risk-unit action budgets ([#806](#806)) and automated reporting ([#245](#245)) ([#1063](#1063)) ([4689816](4689816)) * fine-tuning pipeline + CompositeBackend + workflow lifecycle ([#1065](#1065)) ([85b05bc](85b05bc)), closes [#1001](#1001) [#850](#850) [#1058](#1058) * memory consolidation upgrades (LLM Merge, Search-and-Ask, diversity penalty, distillation capture) ([#1071](#1071)) ([174e2be](174e2be)), closes [#704](#704) * migrate web dashboard from Radix UI to Base UI, activate CSP nonce, rebuild org chart page, and fix agent routing ([#1083](#1083)) ([ebc6921](ebc6921)) * v0.7.0 engine foundations -- structured failure diagnosis + auditable decisions ([#1072](#1072)) ([d341d37](d341d37)) * workflow templates and versioning with diff and rollback ([#1069](#1069)) ([7af94de](7af94de)), closes [#1006](#1006) [#1008](#1008) ### Documentation * unify REST API docs under /docs/openapi/ and patch sitemap ([#1073](#1073)) ([af19382](af19382)) ### Maintenance * bump hypothesis from 6.151.10 to 6.151.11 in the all group ([#1086](#1086)) ([3176318](3176318)) * bump nginxinc/nginx-unprivileged from `f99cc61` to `601c823` in /docker/web in the all group ([#1085](#1085)) ([5eb99ac](5eb99ac)) * bump the all group in /web with 3 updates ([#1087](#1087)) ([8deae44](8deae44)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
Adds a Python CLI tool to analyze and reorganize GitHub issues by priority, scope, and version.
Features
python scripts/issue_analyzer.py analyze- Full analysis of all open issuespython scripts/issue_analyzer.py summary- Brief summary by versionpython scripts/issue_analyzer.py critical- Show only critical/high priority issuespython scripts/issue_analyzer.py propose- Propose new labels/versionspython scripts/issue_analyzer.py deps- Show dependency chainsAnalysis Capabilities
Usage
Notes
ghCLI to be authenticatedReviewed by: ruff, format, secrets detection (all passed)