Skip to content

fix: link rule IDs to their documentation in terminal output - #520

Merged
shenxianpeng merged 2 commits into
mainfrom
fix/link-rule-ids-to-docs
Aug 4, 2026
Merged

fix: link rule IDs to their documentation in terminal output#520
shenxianpeng merged 2 commits into
mainfrom
fix/link-rule-ids-to-docs

Conversation

@shenxianpeng

@shenxianpeng shenxianpeng commented Aug 4, 2026

Copy link
Copy Markdown
Member

The rule ID now carries an OSC 8 hyperlink to its section of the rules reference, so the ID itself is what you click — the same mechanism ruff uses for its codes.

Verified against ruff rather than assumed: under a terminal it advertises support for, it emits

ESC]8;;https://docs.astral.sh/ruff/rules/unused-import ESC\ F401 ESC]8;;ESC\

and prints no separate documentation line, which is why its output stays compact.

The URL still has to survive CI

commit-check runs in a lot of places ruff does not, so dropping the Docs: line outright would lose the address wherever the escape cannot render. The line is kept unless the ID is actually a link:

Context Output
iTerm2 / WezTerm / VS Code / kitty / VTE ≥ 0.50 CC001 is clickable, no Docs: line
Piped, redirected, CI log Plain CC001, Docs: line kept
TERM=dumb, unknown terminal Plain CC001, Docs: line kept
FORCE_HYPERLINK=1 Linked regardless

All four were exercised against a real pty, not just unit-tested.

Three things the output got wrong

Visible in any two-failure run:

The blank line closed the suggestion, not the block. Docs: ended up separated from the rule it belonged to and butted against the next one:

Suggest: Use <type>(<scope>): <description>, ...
                                                  ← separator landed here
Docs: https://commit-check.com/rules/#cc001
CC005 subject_min_length check failed ==> test    ← no separation

The name was the config key, not the documented one. Output said subject_min_length; the rules reference titles that section subject-min-length. Reading a name off the terminal and searching the docs for it found nothing. RuleCatalogEntry.name already existed for this and simply was not being used.

The advice for the length rules named no length. Suggest: Provide a meaningful subject (>= configured min) sat directly under Subject must be at least 5 characters — vaguer than the line above it. Both length suggestions are now templated on the same value as the error, so CC004 and CC005 name the actual limit.

Before / after

$ echo "test" | commit-check -m --no-banner    # piped
CC001 message check failed ==> test
The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
Suggest: Use <type>(<scope>): <description>, where <type> is one of: feat, fix, ...
Docs: https://commit-check.com/rules/#cc001

CC005 subject-min-length check failed ==> test
Subject must be at least 5 characters
Suggest: Write a subject of at least 5 characters
Docs: https://commit-check.com/rules/#cc005

Trailing whitespace on the failure and suggestion lines is gone as a side effect of the rewrite.

Testing

$ pytest tests/ -q
472 passed, 1 failed

The failure is test_load_config_file_permission_error, which uses os.chmod(0o000) and cannot fail as root; it reproduces on main.

TestHyperlinks adds 11 cases: the escape shape, terminal detection across nine environments including a malformed VTE_VERSION, that the ID is linked only when supported, that the Docs: line appears exactly when the ID is not a link, and that the blank line closes the block.

Two existing assertions in test_print_error_message were checking for author_email in the output. They now assert author-email and additionally that no underscore survives into the printed name, so the kebab-case contract is pinned rather than merely accommodated.

Not in this PR

The banner says "Commit rejected by Commit-Check." and then "Commit rejected." on either side of the ASCII art, and there is still no summary count of failures. Both change the shape of the output rather than fix a defect in it, so they belong with the Action output rework on the roadmap.

Summary by CodeRabbit

  • Bug Fixes

    • Length-related suggestions now clearly reference the configured minimum or maximum.
    • Error messages consistently display the relevant check name and failure indicator.
    • Improved spacing prevents unnecessary blank lines in suggestions.
  • Usability Improvements

    • Rule identifiers can link directly to documentation in supported terminals.
    • Documentation links remain visible as plain URLs when terminal hyperlinks are unavailable.
    • Check names are displayed in a more readable kebab-case format.

@shenxianpeng
shenxianpeng requested a review from a team as a code owner August 4, 2026 09:46
@github-actions github-actions Bot added bug Something isn't working tests Add test related changes labels Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@shenxianpeng, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 10bf0474-8c86-44c0-b49d-dfcf33a2da0f

📥 Commits

Reviewing files that changed from the base of the PR and between 9817088 and 8e150a4.

📒 Files selected for processing (6)
  • .pre-commit-config.yaml
  • commit_check/rule_builder.py
  • commit_check/rules_catalog.py
  • commit_check/util.py
  • tests/rule_builder_test.py
  • tests/util_test.py
📝 Walkthrough

Walkthrough

The change formats length-rule suggestions with configured limits and adds OSC 8 hyperlink support for validation failure output. It also updates check-name formatting, documentation-link rendering, and related spacing tests.

Changes

Validation message improvements

Layer / File(s) Summary
Length rule suggestion templating
commit_check/rules_catalog.py, commit_check/rule_builder.py
Catalog suggestions for CC004 and CC005 now include length placeholders. _build_length_rule formats those placeholders with the configured limit.
Hyperlinked failure output
commit_check/util.py, tests/util_test.py
Failure output detects OSC 8 support, links rule IDs to documentation when supported, formats check names in kebab-case, and adjusts spacing. Tests cover terminal detection, hyperlinks, plain URLs, and rendered output.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: minor

Sequence Diagram(s)

sequenceDiagram
  participant FailureOutput
  participant print_error_message
  participant supports_hyperlinks
  participant hyperlink
  participant Terminal
  FailureOutput->>print_error_message: Pass rule_id and docs_url
  print_error_message->>supports_hyperlinks: Check OSC 8 support
  supports_hyperlinks-->>print_error_message: Return terminal capability
  alt Hyperlinks supported
    print_error_message->>hyperlink: Link rule ID to documentation
    hyperlink-->>print_error_message: Return OSC 8-formatted ID
  end
  print_error_message->>Terminal: Print formatted failure message
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: linking rule IDs to their documentation in terminal output.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/link-rule-ids-to-docs

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

❤️ Share

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

The rule ID now carries an OSC 8 hyperlink to its section of the rules
reference, the way ruff links its codes, so the ID itself is what you
click. Where that renders, the separate "Docs:" line is dropped: it was
repeating an address the reader already has. Where it does not — a pipe,
a CI log, a terminal that would print the escape as junk — the line stays,
because there it is the only way to reach the address at all.

Three things the previous output got wrong, all visible in a two-failure
run:

The blank line closed the suggestion rather than the block, so "Docs:"
was separated from the rule it belonged to and butted against the next
one. It now closes the block.

The name was printed as the config key, subject_min_length, while the
rules reference titles its sections in kebab-case. Reading a name off the
terminal and searching the documentation for it found nothing. Both now
say subject-min-length.

The advice for the two length rules named no length: "Provide a
meaningful subject (>= configured min)", directly under an error that had
already said "at least 5 characters". The suggestion is now templated on
the same value as the error.
@shenxianpeng
shenxianpeng force-pushed the fix/link-rule-ids-to-docs branch from 9817088 to 5a707e7 Compare August 4, 2026 09:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
commit_check/rule_builder.py (1)

245-249: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add regression coverage for suggestion formatting.

The current test uses a suggestion without placeholders. It does not verify {max_len} or {min_len} substitution. Add a test for both length rules with a configured value.

🤖 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 `@commit_check/rule_builder.py` around lines 245 - 249, Add regression coverage
around the suggestion formatting logic in the rule-builder tests, using
configured suggestions containing both {max_len} and {min_len} placeholders for
each length rule. Assert that the generated suggestions substitute the
configured length value correctly.
🤖 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.

Inline comments:
In `@commit_check/util.py`:
- Around line 322-323: Update the FORCE_HYPERLINK check in the
hyperlink-detection function to return True only when the environment value is
exactly "1", while preserving normal TTY behavior otherwise. Add a regression
test covering FORCE_HYPERLINK="0" with sys.stdout.isatty() returning False and
verify hyperlinks are not forced.

---

Nitpick comments:
In `@commit_check/rule_builder.py`:
- Around line 245-249: Add regression coverage around the suggestion formatting
logic in the rule-builder tests, using configured suggestions containing both
{max_len} and {min_len} placeholders for each length rule. Assert that the
generated suggestions substitute the configured length value correctly.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aef7df59-3e58-487c-b501-efdf5498017b

📥 Commits

Reviewing files that changed from the base of the PR and between 631ae91 and 9817088.

📒 Files selected for processing (4)
  • commit_check/rule_builder.py
  • commit_check/rules_catalog.py
  • commit_check/util.py
  • tests/util_test.py

Comment thread commit_check/util.py Outdated
Comment on lines +322 to +323
if os.environ.get("FORCE_HYPERLINK"):
return True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Treat FORCE_HYPERLINK as an explicit boolean.

Line 322 enables hyperlinks for any nonempty value. FORCE_HYPERLINK=0 therefore emits OSC 8 sequences even for piped output. The PR contract specifies FORCE_HYPERLINK=1 as the forcing value.

Proposed fix
-    if os.environ.get("FORCE_HYPERLINK"):
+    if os.environ.get("FORCE_HYPERLINK") == "1":
         return True

Add a regression test with FORCE_HYPERLINK="0" and sys.stdout.isatty() set to False.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if os.environ.get("FORCE_HYPERLINK"):
return True
if os.environ.get("FORCE_HYPERLINK") == "1":
return True
🤖 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 `@commit_check/util.py` around lines 322 - 323, Update the FORCE_HYPERLINK
check in the hyperlink-detection function to return True only when the
environment value is exactly "1", while preserving normal TTY behavior
otherwise. Add a regression test covering FORCE_HYPERLINK="0" with
sys.stdout.isatty() returning False and verify hyperlinks are not forced.

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.48%. Comparing base (631ae91) to head (8e150a4).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #520      +/-   ##
==========================================
+ Coverage   97.43%   97.48%   +0.05%     
==========================================
  Files          12       12              
  Lines        1207     1231      +24     
==========================================
+ Hits         1176     1200      +24     
  Misses         31       31              

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

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

Any non-empty value counted as on, so FORCE_HYPERLINK=0 — the obvious way
to ask for links to be turned off — turned them on instead. It now follows
what FORCE_COLOR established and what ruff does: 0 disables even on a
terminal that renders links, any other value enables.

Also covers the length-rule substitution that had none. Removing the
.format() call leaves the four new cases failing, so they hold the
placeholder from reaching a user as a literal brace.
@sonarqubecloud

sonarqubecloud Bot commented Aug 4, 2026

Copy link
Copy Markdown

@shenxianpeng shenxianpeng removed the tests Add test related changes label Aug 4, 2026
@shenxianpeng
shenxianpeng merged commit 5c39e5f into main Aug 4, 2026
27 checks passed
@shenxianpeng
shenxianpeng deleted the fix/link-rule-ids-to-docs branch August 4, 2026 10:04
@codspeed-hq

codspeed-hq Bot commented Aug 4, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 12.27%

❌ 9 regressed benchmarks
✅ 390 untouched benchmarks
🆕 28 new benchmarks
⏩ 121 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
test_signoff_validator_missing_signoff 453.8 µs 523.1 µs -13.26%
test_commit_message_validator_invalid_commit 485.4 µs 556.9 µs -12.83%
test_subject_length_validator_max_too_long 479.2 µs 549.1 µs -12.72%
test_subject_length_validator_min_too_short 474.2 µs 542.5 µs -12.59%
test_body_validator_no_body 465.6 µs 531.8 µs -12.45%
test_subject_capitalization_validator_not_capitalized 501.2 µs 569.5 µs -11.99%
test_validation_engine_validate_all_fail 547.2 µs 620.8 µs -11.85%
test_commit_message_validator_custom_pattern_jira 532.3 µs 601.2 µs -11.47%
test_commit_message_validator_custom_pattern_github_issue 534.6 µs 602.2 µs -11.23%
🆕 test_blank_line_closes_the_block N/A 2.5 ms N/A
🆕 test_docs_line_dropped_when_id_is_the_link N/A 2.5 ms N/A
🆕 test_docs_line_kept_without_hyperlinks N/A 2.5 ms N/A
🆕 test_forced_even_when_piped N/A 2.8 ms N/A
🆕 test_hyperlink_wraps_text_in_osc8 N/A 248.4 µs N/A
🆕 test_id_is_linked_when_supported N/A 2.3 ms N/A
🆕 test_id_is_plain_when_unsupported N/A 2.3 ms N/A
🆕 test_not_supported_when_piped N/A 2.8 ms N/A
🆕 test_terminal_detection[env0-True] N/A 3 ms N/A
🆕 test_terminal_detection[env1-True] N/A 3 ms N/A
🆕 test_terminal_detection[env2-True] N/A 2.9 ms N/A
... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/link-rule-ids-to-docs (8e150a4) with main (631ae91)

Open in CodSpeed

Footnotes

  1. 121 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@shenxianpeng shenxianpeng mentioned this pull request Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant