Skip to content

[Auto] Add rule-set profiles: one-line curated rule bundles (profile: claude-5) - #495

Open
stbenjam wants to merge 8 commits into
mainfrom
claude/rule-set-profiles-ctwjla
Open

[Auto] Add rule-set profiles: one-line curated rule bundles (profile: claude-5)#495
stbenjam wants to merge 8 commits into
mainfrom
claude/rule-set-profiles-ctwjla

Conversation

@stbenjam

@stbenjam stbenjam commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Implements the P9 proposal from #444: a profile config key that selects a named, curated bundle of rule overrides with one line instead of a hand-maintained rules: block.

version: "0.19.0"
profile: claude-5

Design

  • Profiles live in a new data-only registry, src/skillsaw/profiles.py. A Profile is a name, description, and a rule_id → {enabled, severity, params} mapping — adding a future profile is a single dict entry with a rationale comment per rule.
  • Overrides merge in a strict precedence chain: builtin defaults < profile < user rules: entries. Merging happens in LinterConfig.get_rule_config() / rule_enabled_reason(), so lint, fix, and skillsaw explain all pick it up; explain reports profile-driven state ("enabled: true set by profile 'claude-5'").
  • TestProfileRegistry pins the registry's integrity: every profile must name live, canonical (non-aliased, non-deprecated) rules with valid severities/enabled values, and any parameter it sets must exist in that rule's config_schema. A bad entry fails CI instead of being silently ignored at lint time.

Profiles

default — empty by definition; identical to omitting the key (pinned by a byte-identical-output integration test).

claude-5 — encodes Anthropic's Claude 5 context-engineering guidance as deltas over the defaults, per the #444 gap analysis:

Rule Change Rationale
content-repeated-directive severity → error "Eliminate duplicate instructions" — state it once
content-instruction-drift severity → warning Same shift, cross-file
content-tautological severity → warning Remove guidance modern models exhibit by default
content-section-length severity → warning Progressive disclosure: split long content
context-budget claude-md/agents-md/gemini-md 3k warn / 8k error; skill 2k / 5k "Keep your CLAUDE.md lightweight"
content-missing-stop-condition enabled: true Open-ended loops burn autonomous sessions
content-weak-language enabled: false Judgment-delegating prose is the recommended style; hedge-flagging fights it (dominated FP count in the #484 calibration)

Semantics

  • Any explicit user enabled — including auto — replaces the profile's decision for that rule.
  • A severity-only user override does not re-enable a profile-disabled rule; re-enabling takes an explicit enabled.
  • Profile enabled decisions bypass the config version gate (profiles ship with the installed skillsaw, so selecting one is an explicit opt-in); severity/parameter-only entries never change activation.
  • Profiles cannot resurrect deprecated rules.
  • Unknown profile names fail config load with the available list; older skillsaw versions reading a config with profile: degrade to the existing unknown-key warning.

Docs

  • docs/configuration.md: new Profiles section with precedence rules and the full claude-5 delta table.
  • README mention; regenerated .skillsaw.yaml.example now advertises the key (# profile: claude-5).

Validation

  • make test: 3960 passed (26 new unit tests incl. TestProfileRegistry; 5 new integration tests over a new realistic tests/fixtures/config/profile-claude-5 fixture whose one CLAUDE.md exercises an elevation, a disable, and an opt-in enable under both profiles)
  • make lint, make update clean
  • openshift-eng/ai-helpers: output byte-identical to main (the exit-1 there is pre-existing and self-inflicted — their own custom plugins-doc-up-to-date rule flags their docs/ as stale on unmodified main too; grade and violation counts match main exactly, confirming the default profile changes nothing)

Closes #444's P9. The remaining proposals in #444 (new rules P1–P8) are untouched; when they land, adding them to the claude-5 profile is a one-line registry edit each.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JW4e6YDBtnqNNM1F7Hy1gg


Generated by Claude Code

Summary by CodeRabbit

  • New Features

    • Added configurable rule profiles, including the claude-5 profile.
    • Profiles can adjust rule severity, enablement, context limits, and language handling.
    • Explicit project settings take precedence over profile values.
    • skillsaw explain identifies profile-provided severity values.
    • Generated configurations include profile details and guidance.
  • Documentation

    • Added profile configuration examples, available profiles, merge behavior, and versioning guidance.
    • Documented support for rule plugins and local rules.
  • Bug Fixes

    • Added clear validation errors for unknown profiles and invalid settings.

Introduce a 'profile' config key selecting a named bundle of rule
overrides applied between the builtin defaults and the user's own
rules: entries (defaults < profile < user). Two profiles ship:

- default: the builtin defaults, unchanged (and what runs when the
  key is absent)
- claude-5: tuned to Anthropic's Claude 5 context-engineering
  guidance — elevates content-repeated-directive to error and
  content-instruction-drift / content-tautological /
  content-section-length to warning, tightens context-budget limits
  for claude-md/agents-md/gemini-md and skills, enables the opt-in
  content-missing-stop-condition rule, and disables
  content-weak-language

Semantics: any explicit user 'enabled' (including auto) replaces the
profile's decision; a severity-only user override does not re-enable a
profile-disabled rule; profile 'enabled' decisions bypass the config
version gate (profiles ship with the installed skillsaw) while
severity/parameter-only entries never change activation; deprecated
rules cannot be resurrected by a profile. skillsaw explain reports
when a rule's state comes from the active profile.

The registry in skillsaw/profiles.py is data-only so future profiles
are a single Profile entry; TestProfileRegistry pins that every
profile names live, canonical rules with valid values and declared
config parameters.

Closes the P9 proposal from #444.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JW4e6YDBtnqNNM1F7Hy1gg
@qodo-code-review

Copy link
Copy Markdown

ⓘ Your Qodo trial ends soon. Ask your workspace admin to set up billing to keep reviews running after the trial. Manage billing

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add config profile for curated rule-set bundles (default, claude-5)

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add profile: config key to apply curated rule overrides with one line.
• Merge precedence is defaults < profile < user rules: with clear enablement semantics.
• Document profiles and add unit/integration tests to pin registry validity and behavior.
Diagram

graph TD
  A[".skillsaw.yaml"] --> B["LinterConfig.from_file"] --> C["Active profile"] --> D["Rule config merge"] --> E["rule_enabled_reason"] --> F["lint / fix / explain"]
  G["profiles.py registry"] --> C
  H["Builtin defaults"] --> D
  I["User rules overrides"] --> D
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Config `extends` / include-based rule templates
  • ➕ Allows teams to maintain/share profiles outside the binary (repo-local or remote)
  • ➕ Naturally supports composing multiple templates
  • ➖ Harder to make deterministic/secure (remote fetch, path resolution)
  • ➖ More complex UX and error cases than a single validated name
2. Ship profiles as a built-in rules plugin
  • ➕ Keeps presets modular and versioned like other extensions
  • ➕ Could allow third-party curated profiles
  • ➖ More moving parts for a “data-only preset” feature
  • ➖ Would still need precedence/enablement semantics in core config layer

Recommendation: The current built-in, data-only registry + explicit profile: selection is the best fit for a small number of blessed presets: it’s deterministic, easy to validate (CI-pinned registry integrity), and applies uniformly across lint/fix/explain via the config layer. Consider extends/composition only if there’s a clear roadmap for user-defined or multi-profile stacking.

Files changed (10) +597 / -9

Enhancement (2) +175 / -7
config.pyParse 'profile', merge profile overrides, and explain enablement reasons +64/-7

Parse 'profile', merge profile overrides, and explain enablement reasons

• Adds 'profile' to 'LinterConfig', validates it on load, and includes it in save/to_dict behavior. Implements profile rule overrides merged between defaults and user overrides, and updates 'rule_enabled_reason' so profile 'enabled' decisions apply before version gating while preventing resurrection of deprecated rules; reasons now report profile-driven enable/disable.

src/skillsaw/config.py

profiles.pyIntroduce built-in profile registry (default, claude-5) +111/-0

Introduce built-in profile registry (default, claude-5)

• Adds a frozen 'Profile' dataclass and a 'PROFILES' registry with 'available_profiles()'. Implements 'default' as empty/no-op and 'claude-5' as a curated set of severity/enablement/parameter overrides (including context-budget limits) with per-rule rationale comments.

src/skillsaw/profiles.py

Tests (5) +364 / -0
.skillsaw.yamlAdd integration fixture config selecting 'claude-5' profile +2/-0

Add integration fixture config selecting 'claude-5' profile

• Creates a minimal config fixture that opts into the 'claude-5' profile for end-to-end verification.

tests/fixtures/config/profile-claude-5/.skillsaw.yaml

CLAUDE.mdAdd fixture content to trigger profile-specific rule behavior +28/-0

Add fixture content to trigger profile-specific rule behavior

• Adds a CLAUDE.md fixture containing repeated directives, weak/hedging language, and an open-ended instruction to exercise severity elevation, rule disablement, and opt-in enablement under the profile.

tests/fixtures/config/profile-claude-5/CLAUDE.md

README.mdAdd minimal README fixture for the integration repo +3/-0

Add minimal README fixture for the integration repo

• Adds a small README referencing CLAUDE.md to complete the integration fixture repository structure.

tests/fixtures/config/profile-claude-5/README.md

test_config.pyUnit tests for profile parsing, precedence, and registry integrity +254/-0

Unit tests for profile parsing, precedence, and registry integrity

• Adds tests covering defaulting behavior, validation errors, merge precedence, mutation safety (deep copies), enablement semantics (explicit enabled/auto beats profile; severity-only doesn’t re-enable), version-gate bypass for profile-enabled rules, and invariants for the profile registry (canonical/non-deprecated rule IDs and valid override keys/values).

tests/test_config.py

test_integration.pyIntegration tests for end-to-end profile effects on lint output +77/-0

Integration tests for end-to-end profile effects on lint output

• Adds an integration test suite asserting that 'profile: claude-5' reshapes results (severity changes, opt-in enabled rule runs, disabled rule suppressed), that 'profile: default' is a byte-identical no-op, that user 'rules:' overrides win, and that unknown profiles fail with a friendly error.

tests/test_integration.py

Documentation (2) +54 / -2
README.mdHighlight profiles as a one-line configuration option +4/-2

Highlight profiles as a one-line configuration option

• Updates the product description to mention profiles and links to the configuration docs for profiles, calling out 'claude-5' as an example preset.

README.md

configuration.mdAdd Profiles section with precedence and semantics +50/-0

Add Profiles section with precedence and semantics

• Documents 'profile:' syntax, merge precedence (defaults < profile < user rules), override semantics, and the shipped profiles ('default', 'claude-5') including rationale and the version-gate bypass rules for profile-driven enablement.

docs/configuration.md

Other (1) +4 / -0
.skillsaw.yaml.exampleAdvertise 'profile:' usage in example config +4/-0

Advertise 'profile:' usage in example config

• Adds commented documentation for the new 'profile' key and lists available profiles (default, claude-5) above the 'rules:' section.

.skillsaw.yaml.example

@stbenjam

stbenjam commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

I am not sure about the profile mechanism in the YAML config file -- I was imagining this just being skillsaw init feature to start off on a baseline, but then you wouldn't get updated guidance. Perhaps claude-6 changes, and folks can just update their profile to adopt the latest... list the pros/cons of both approaches

We should add a gpt-5.6 profile as well https://developers.openai.com/api/docs/guides/latest-model

Also perhaps we should alias "claude-latest" and "gpt-latest" to the newest one, as a moving target.

@stbenjam stbenjam changed the title Add rule-set profiles: one-line curated rule bundles (profile: claude-5) [Auto] Add rule-set profiles: one-line curated rule bundles (profile: claude-5) Aug 7, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 30d35f9efe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/skillsaw/config.py
if self.profile and self.profile != DEFAULT_PROFILE:
f.write(f"profile: {self._yaml_value(self.profile)}\n\n")
else:
f.write("# profile: claude-5\n\n")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep generated defaults from shadowing the selected profile

When a user follows this generated comment—or edits .skillsaw.yaml.example—by uncommenting profile: claude-5, the file already contains explicit enabled and severity entries for every rule from LinterConfig.default(). Since user rule entries take precedence, these generated defaults suppress the profile's severity changes and its enable/disable decisions; only the parameter override survives. Consequently, skillsaw init users cannot activate the advertised one-line bundle without deleting the relevant generated rule entries, so the generated configuration needs to preserve profile applicability rather than advertising this uncomment as sufficient.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good catch — addressed in e418d17 by making the advertisement honest rather than restructuring generated output: the generated header (and a new docs note) now says the explicit per-rule entries below count as user overrides and win over the profile, so adopting one also means deleting the entries the profile should manage. Changing skillsaw init to stop materializing per-rule defaults, or making profiles beat default-identical user entries, would change long-standing generated-config semantics — left as a maintainer call.


Generated by Claude Code

Comment thread src/skillsaw/config.py
Comment on lines +149 to +151
raw_profile = data.get("profile")
if raw_profile is None:
profile = DEFAULT_PROFILE

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject an explicitly empty profile value

When the config contains profile: with no value, YAML parses it as null and this branch silently selects the default profile. That makes an incomplete or accidentally blank profile declaration run a different rule set without warning, even though every other non-string profile value is rejected as invalid and the error states that the value must be one of the named profiles. Distinguish an absent key from an explicitly null value and reject the latter.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Keeping null-as-absent deliberately: it matches this codebase's convention for every other key — fail-on: (null) behaves like the default (pinned by test_null_fail_on_does_not_crash), version: (null) falls back with a warning, and null rule entries coerce to {}. Rejecting null only for profile would be the inconsistency. e418d17 pins the behavior with test_null_profile_behaves_like_default so it's a documented choice rather than an accident.


Generated by Claude Code

@qodo-code-review

qodo-code-review Bot commented Aug 7, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Review-history comment in profile ✓ Resolved 📜 Skill insight ⚙ Maintainability
Description
The new claude-5 profile includes a comment referencing calibration results and violation counts
from issue #484, which is review-history residue that won’t make sense to future readers and can
become stale. This violates the requirement that comments describe shipped code rather than the
review/calibration process that produced it.
Code

src/skillsaw/profiles.py[R92-95]

+        # hedges fights it. Calibration against anthropics/
+        # claude-plugins-official (#484) showed this rule dominating the
+        # violation count on Anthropic's own flagship plugins.
+        "content-weak-language": {"enabled": False},
Relevance

●●● Strong

Team prefers durable comments/docs; has accepted removing stale/history-anchored wording and
references.

PR-#485
PR-#405

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2344941 forbids comments that describe the review/calibration process or include
round-scoped measurements. The added comment explicitly cites calibration and violation-count
outcomes from #484, which is review-history residue rather than a durable explanation of the
code’s behavior.

src/skillsaw/profiles.py[90-95]
Skill: skillsaw-review-panel

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A newly added comment embeds review/calibration history (issue numbers and "violation count" results). Comments should explain stable rationale for shipped behavior, not narrate historical review outcomes or round-scoped measurements.

## Issue Context
The `claude-5` profile is a curated configuration bundle intended to be maintained over time. References like "Calibration ... showed this rule dominating the violation count" are likely to become stale and fail the "stranger test".

## Fix Focus Areas
- src/skillsaw/profiles.py[90-95]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Nested config merge loss ✓ Resolved 🐞 Bug ≡ Correctness
Description
LinterConfig.get_rule_config() shallow-merges defaults/profile/user dicts, so when both profile
and user set a nested parameter (e.g. context-budget.limits), the user's partial dict replaces the
entire profile dict and silently drops other profile-set nested values. This contradicts the
documented expectation that unmentioned fields keep their default/profile-set values and can
unintentionally loosen limits back toward rule defaults for categories the user didn’t touch.
Code

src/skillsaw/config.py[396]

+        merged = {**defaults, **profile_overrides, **overrides}
Relevance

●●● Strong

Config semantics bug: shallow merge drops nested profile/default keys; repo often accepts config.py
correctness fixes.

PR-#300

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The merge line in get_rule_config() is a shallow merge, and the claude-5 profile uses a nested
limits dict for context-budget. The rule’s own defaults include many categories, so losing
profile-provided nested keys causes those categories to fall back to DEFAULT_LIMITS rather than
the selected profile’s tighter settings.

src/skillsaw/config.py[376-397]
src/skillsaw/profiles.py[74-86]
src/skillsaw/rules/builtin/context_budget/budget.py[16-32]
src/skillsaw/rules/builtin/context_budget/budget.py[83-90]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`LinterConfig.get_rule_config()` merges config layers with a shallow dict merge. This breaks profile semantics for nested rule parameters: if a profile sets `limits` keys A/B/C and the user overrides only key A, the user dict replaces the entire `limits` mapping and keys B/C are lost (reverting to each rule’s internal defaults).

## Issue Context
Profiles are explicitly described as applying between builtin defaults and user `rules:` entries, and the code comment says unmentioned fields keep their default/profile-set values. The `claude-5` profile sets `context-budget.limits` as a nested dict (subset of categories), making this behavior user-visible.

## Fix Focus Areas
- src/skillsaw/config.py[369-397]
- tests/test_config.py[1334-1361]

## Implementation notes
- Introduce a small recursive merge helper for mappings (e.g., `deep_merge(base, overlay)`), where:
 - if both `base[k]` and `overlay[k]` are dicts, merge recursively;
 - otherwise, `overlay[k]` replaces `base[k]`.
- Apply it in precedence order: `defaults` -> `profile_overrides` -> `user_overrides`.
- Add a regression test showing that with `profile="claude-5"` and a user override like:
 - `rules={"context-budget": {"limits": {"skill": {"warn": 1000}}}}`
 the effective config preserves the profile’s `claude-md`/`agents-md`/`gemini-md` limits (and only changes `skill.warn`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 171 rules
✅ Skills: 6 invoked
  skillsaw-pr-review
  skillsaw-issue-solver
  skillsaw-pr-followup
  skillsaw-create-plugin
  skillsaw-review-panel
  skillsaw-maintenance

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread src/skillsaw/profiles.py Outdated
Comment thread src/skillsaw/config.py Outdated
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9138776f-5d7d-4c0c-ad3b-5609818b2c9e

📥 Commits

Reviewing files that changed from the base of the PR and between 35cbead and 3601d50.

📒 Files selected for processing (1)
  • tests/test_config.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_config.py

📝 Walkthrough

Walkthrough

Changes

The PR adds named rule profiles, including claude-5. Configuration loading validates profiles, merges profile settings with defaults and user rules, and adjusts version behavior. The CLI, generated configuration, documentation, and tests now cover profile support.

Rule profile support

Layer / File(s) Summary
Profile registry and Claude 5 settings
src/skillsaw/profiles.py
Adds the Profile model, registry, deterministic listing, and default and claude-5 profiles.
Profile parsing and rule resolution
src/skillsaw/config.py
Adds profile validation, profile-dependent version selection, recursive merging, enablement handling, serialization, and user-override precedence.
Generated configuration and documentation
.skillsaw.yaml.example, README.md, docs/configuration.md, src/skillsaw/config.py
Documents profile syntax, available profiles, precedence, version behavior, and generated configuration output.
Profile attribution in explain output
src/skillsaw/cli/_explain.py, tests/test_explain.py
Attributes profile-provided severities in skillsaw explain and preserves unannotated output for explicit user overrides.
Profile behavior validation
tests/test_config.py, tests/test_integration.py
Tests parsing, registry validation, merging, version behavior, serialization, rule activation, precedence, compatibility, and invalid profiles.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant LinterConfig
  participant PROFILES
  participant RuleResolver
  User->>LinterConfig: Select profile
  LinterConfig->>PROFILES: Validate and load profile
  LinterConfig->>RuleResolver: Resolve rule settings
  RuleResolver->>PROFILES: Read profile overrides
  RuleResolver-->>LinterConfig: Return merged settings
  LinterConfig-->>User: Report effective rule severity
Loading

Possibly related PRs

  • stbenjam/skillsaw#300: Both changes modify configuration loading and rule/version-selection behavior.
  • stbenjam/skillsaw#479: The claude-5 profile configures rule severities and enablement that may include rules introduced by this PR.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR adds the profile, but it does not show implementation of the linked issue's required rule changes, actionability fix, or regression fixtures. Implement or link the missing rule changes, content-actionability correction, regression fixtures, and source documentation required by issue #444.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding curated rule-set profiles with a claude-5 profile.
Out of Scope Changes check ✅ Passed The code, tests, and documentation changes support the profile implementation and the stated linked issue objectives.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/rule-set-profiles-ctwjla

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

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.00%. Comparing base (579493c) to head (3601d50).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #495      +/-   ##
==========================================
+ Coverage   93.88%   94.00%   +0.11%     
==========================================
  Files         173      174       +1     
  Lines       14715    14771      +56     
==========================================
+ Hits        13815    13885      +70     
+ Misses        900      886      -14     

☔ 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.

@stbenjam

stbenjam commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Another question: if a user omits skillsaw version but specifies profile, should they opt-in automatically to future updates (allowing breaking changes, but also getting the latest guidance enforced immediately). Typically omitting version forces config back to 0.6.0, but with profile we know they have the latest

…n, init caveat

- Deep-merge nested rule parameters across config layers (defaults <
  profile < user) so a user overriding one entry of a mapping like
  context-budget 'limits' keeps the profile's other entries instead of
  silently reverting them to rule defaults (Qodo).
- A config that selects a curated profile but omits 'version' now
  follows the installed skillsaw version instead of the 0.6.0 fallback
  — choosing a profile opts into the current rule set; 'profile:
  default' keeps the legacy fallback (stbenjam).
- Clarify the generated-config profile hint and docs: the explicit
  per-rule entries skillsaw init writes count as user overrides and
  shadow the profile, so adopting one there also means deleting the
  entries the profile should manage (Codex P1).
- Pin 'profile:' (null) behaving like an absent key, matching the
  fail-on/version convention (Codex P2), and cover the defensive
  unknown-profile branch (codecov).
- Trim review-history references from the claude-5 profile comments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JW4e6YDBtnqNNM1F7Hy1gg

stbenjam commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Implemented in e418d17: a config that selects a curated profile but omits version now follows the installed version instead of the 0.6.0 fallback, so profile: claude-5 alone is a fully working one-liner — new rules apply immediately after upgrades, and setting version explicitly still pins the rule set. The load warning changes accordingly ("'profile: claude-5' implies the installed version (X.Y.Z)…") so the opt-in is visible rather than silent.

One scoping choice: profile: default keeps the legacy 0.6.0 fallback. It's documented as byte-identical to having no profile key (there's a test pinning that), and "default" doesn't signal wanting the latest guidance the way a curated profile does. Easy to widen later if you'd rather any explicit profile imply latest.

The same push also addresses the bot findings: nested rule parameters now deep-merge across config layers (a user overriding one limits entry keeps the profile's others), and the generated-config # profile: hint plus docs now say that init-generated per-rule entries shadow the profile.


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e418d17201

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/configuration.md
Comment on lines +132 to +134
For the same reason, a config that selects a curated profile but omits
`version` follows the **installed** skillsaw version rather than falling
back to `0.6.0`: new rules apply immediately after upgrades. Set `version`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reconcile the blanket missing-version warning

This exception contradicts the prominent warning above that every config without a version is treated as 0.6.0. With profile: claude-5, LinterConfig.from_file() instead assigns the installed version, so readers may incorrectly believe an unversioned profile remains pinned across upgrades; qualify the earlier warning to exclude curated profiles or cross-reference this exception.

AGENTS.md reference: AGENTS.md:L27-L31

Useful? React with 👍 / 👎.

claude added 2 commits August 7, 2026 11:33
The Version Pinning warning stated every unversioned config falls back
to 0.6.0, contradicting the curated-profile exception documented in the
Profiles section. Cross-reference the exception where the blanket claim
is made.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JW4e6YDBtnqNNM1F7Hy1gg

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87f7ab4a39

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/skillsaw/config.py Outdated
Comment on lines +638 to +639
if self.profile and self.profile != DEFAULT_PROFILE:
f.write(f"profile: {self._yaml_value(self.profile)}\n\n")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep fallback profiles reloadable when saving

When an API caller constructs LinterConfig(profile="not-a-profile"), the new test explicitly treats that value as a supported fallback to the default profile, but save() writes profile: not-a-profile; subsequently loading the generated file raises ValueError because from_file() rejects unknown profiles. This breaks the config save/load round trip, so either validate programmatic profile assignments before serialization or serialize their effective default-profile value.

Useful? React with 👍 / 👎.

Comment thread src/skillsaw/config.py Outdated
Comment on lines +529 to +533
profile_enabled = self._profile_rules(rule_id).get("enabled")
if profile_enabled is True:
return True, f"enabled: true set by profile '{self.profile}'"
if profile_enabled is False:
return False, f"disabled by profile '{self.profile}'"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bypass version gating for profile-set auto

When a current or future profile sets a rule to enabled: auto—a value explicitly accepted by the new profile-registry validation—this branch handles neither boolean case and later applies the config version gate because there are no user overrides. That contradicts the documented rule that profile enabled decisions bypass version pinning, so an older pinned config can silently skip a rule that its selected profile opted into; preserve the profile-origin flag while falling through to repository/format detection and use it to bypass the version check.

Useful? React with 👍 / 👎.

…ile auto

- save()/to_dict() no longer serialize a programmatically-assigned
  profile name that isn't in the registry — it contributes no overrides,
  and writing it produced a file from_file() rejects.
- A profile-set 'enabled: auto' now bypasses the config version gate on
  its way to repo-type/format detection, matching the documented rule
  that profile enabled decisions are version-independent (true/false
  already behaved this way; no shipped profile uses auto yet, but the
  registry validation accepts it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JW4e6YDBtnqNNM1F7Hy1gg

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 93ce60d8df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/skillsaw/config.py Outdated
version: str = ""
# Named rule-set profile (see skillsaw.profiles): a curated bundle of
# rule overrides applied under the user's own ``rules:`` entries.
profile: str = DEFAULT_PROFILE

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the existing positional constructor slots

Adding profile before rules silently changes the generated dataclass constructor: an existing call such as LinterConfig("0.19.0", {"my-rule": {"enabled": true}}) previously populated rules, but now stores the dict in profile; a subsequent get_rule_config() then passes that unhashable dict to PROFILES.get() and raises TypeError. Append the new field after the existing constructor fields or otherwise preserve the prior positional signature.

Useful? React with 👍 / 👎.

Comment thread docs/configuration.md Outdated
Comment on lines +102 to +103
`enabled` setting. `skillsaw explain <rule-id>` reports when a rule's
state comes from the active profile.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Limit the explain attribution claim to enablement

This promises profile attribution for a rule's general state, but rule_enabled_reason() only names the profile for profile-supplied enabled: true or enabled: false. For current severity- or parameter-only entries such as content-repeated-directive and context-budget, skillsaw explain displays the effective value without saying it came from claude-5, so the documented diagnostic behavior is unavailable for most profile overrides. Describe this as enablement attribution or extend explain to attribute the other profile layers.

Useful? React with 👍 / 👎.

claude added 2 commits August 7, 2026 13:26
…verity in explain

- Declare the profile dataclass field after every pre-profile field so
  LinterConfig keeps the positional constructor signature of earlier
  releases — positional callers passing (version, rules) no longer put
  the rules dict into profile.
- skillsaw explain now annotates a profile-supplied severity ('severity:
  error (set by profile claude-5)'), dropped when a user rules: entry
  overrides it, and the docs claim is scoped to enablement and severity
  attribution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JW4e6YDBtnqNNM1F7Hy1gg

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_config.py (1)

1525-1530: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Validate profile parameter values.

config_schema only declares "limits" as a "dict". The test does not validate its nested shape or values. A malformed context-budget.limits value can reach _get_limits() and fail during rule execution. Add schema-based validation or assert the expected warn and error mappings explicitly.

🤖 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 `@tests/test_config.py` around lines 1525 - 1530, Extend the profile parameter
validation in the test’s config_schema branch to validate the nested shape and
values of context-budget.limits, not just that the key exists. Assert that
limits is a mapping containing the expected warn and error entries with valid
values, or reuse the project’s schema-based validator if available, so malformed
limits are rejected before rule execution.
🤖 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.

Outside diff comments:
In `@tests/test_config.py`:
- Around line 1525-1530: Extend the profile parameter validation in the test’s
config_schema branch to validate the nested shape and values of
context-budget.limits, not just that the key exists. Assert that limits is a
mapping containing the expected warn and error entries with valid values, or
reuse the project’s schema-based validator if available, so malformed limits are
rejected before rule execution.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 13d08fcd-3890-4f6b-977b-5d5f7316b059

📥 Commits

Reviewing files that changed from the base of the PR and between 87f7ab4 and 35cbead.

📒 Files selected for processing (6)
  • docs/configuration.md
  • src/skillsaw/cli/_explain.py
  • src/skillsaw/config.py
  • tests/test_config.py
  • tests/test_explain.py
  • tests/test_integration.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/configuration.md
  • src/skillsaw/config.py
  • tests/test_integration.py

The registry test checked that profile parameters exist in the rule's
config_schema but not that nested values are well-formed — a malformed
context-budget limits entry in a future profile would only fail at lint
time. Assert each category is an int or a non-empty {warn, error}
mapping of positive ints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JW4e6YDBtnqNNM1F7Hy1gg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Align skillsaw with Anthropic's Claude 5 context-engineering guidance (gap analysis vs #429)

2 participants