Problem
The ## Assigning kind: rubric in CLAUDE.md (added in #54 / #70) defines the four kind: values but does not state the meta-rule that governs them: kind is judged at the target hop, not on historical lineage. The same API can be deprecation in one version file and breaking in the next.
Surfaced during code review of #73 (rails-40 classification): SCOPE_WITHOUT_LAMBDA was deprecated in Rails 3.1 and raises in 4.0. Reviewer asked why it is breaking in rails-40-patterns.yml rather than deprecation. The answer (judge at target hop) is correct but is not written down anywhere — it lives only in the deprecation bullet's "works at this hop" phrase.
Proposed change
Add a meta-rule paragraph to the ## Assigning kind: section in CLAUDE.md, above the four-value list:
Judge kind at the target hop, not historical timeline. A removal that was deprecated in earlier versions is breaking in the file for the version where it actually raises. The same API can be deprecation in rails-31-patterns.yml and breaking in rails-40-patterns.yml. Apply the same rule to all four kinds: kind reflects what the change is at this hop, not what it was at an earlier hop or will become at a later one.
Optionally clarify each of the four values with a "judged at target hop" framing so the rule is reinforced inline:
breaking — Raises / removed / won't boot at the target version.
deprecation — Emits a warning at the target version; removal scheduled later.
migration — Works today at the target version, no warning, recommended migration target.
optional — Opt-in at the target version.
Also document the dependencies: ↔ breaking relationship
Surfaced during code review of #74 (rails-50). Reviewer asked: does the migration kind drive the dependencies: section? Answer is no, but it is not written down. Add a short note to CLAUDE.md clarifying:
- The top-level
dependencies: section in each rails-*-patterns.yml file lists bridge / compatibility gems that rescue breaking patterns until the user migrates. Examples: protected_attributes rescues attr_accessible, rails-controller-testing rescues assigns / assert_template, activerecord-deprecated_finders rescues removed dynamic finders.
dependencies: is only relevant to breaking kind. The other three kinds resolve in code:
deprecation → fix before next hop, see per-pattern fix: field.
migration → rewrite call at user's pace, see per-pattern fix: field.
optional → ignore unless user wants the new feature.
- A
breaking pattern that has a corresponding bridge gem in dependencies: is a softenable break: the user can install the gem to keep shipping while they migrate. A breaking pattern with no bridge entry must be fixed before the version bump.
This relationship is implicit today and was inferred during review rather than read from a doc.
Why
Out of scope
- Reclassifying any existing
kind: value. This is a doc-only PR.
- Changes to the validator, output, or top-level key rename.
References
Problem
The
## Assigning kind:rubric inCLAUDE.md(added in #54 / #70) defines the fourkind:values but does not state the meta-rule that governs them:kindis judged at the target hop, not on historical lineage. The same API can bedeprecationin one version file andbreakingin the next.Surfaced during code review of #73 (rails-40 classification):
SCOPE_WITHOUT_LAMBDAwas deprecated in Rails 3.1 and raises in 4.0. Reviewer asked why it isbreakinginrails-40-patterns.ymlrather thandeprecation. The answer (judge at target hop) is correct but is not written down anywhere — it lives only in the deprecation bullet's "works at this hop" phrase.Proposed change
Add a meta-rule paragraph to the
## Assigning kind:section inCLAUDE.md, above the four-value list:Optionally clarify each of the four values with a "judged at target hop" framing so the rule is reinforced inline:
breaking— Raises / removed / won't boot at the target version.deprecation— Emits a warning at the target version; removal scheduled later.migration— Works today at the target version, no warning, recommended migration target.optional— Opt-in at the target version.Also document the
dependencies:↔breakingrelationshipSurfaced during code review of #74 (rails-50). Reviewer asked: does the
migrationkind drive thedependencies:section? Answer is no, but it is not written down. Add a short note toCLAUDE.mdclarifying:dependencies:section in eachrails-*-patterns.ymlfile lists bridge / compatibility gems that rescuebreakingpatterns until the user migrates. Examples:protected_attributesrescuesattr_accessible,rails-controller-testingrescuesassigns/assert_template,activerecord-deprecated_findersrescues removed dynamic finders.dependencies:is only relevant tobreakingkind. The other three kinds resolve in code:deprecation→ fix before next hop, see per-patternfix:field.migration→ rewrite call at user's pace, see per-patternfix:field.optional→ ignore unless user wants the new feature.breakingpattern that has a corresponding bridge gem independencies:is a softenable break: the user can install the gem to keep shipping while they migrate. Abreakingpattern with no bridge entry must be fixed before the version bump.This relationship is implicit today and was inferred during review rather than read from a doc.
Why
breakingvsdeprecationborderline calls by a single rule rather than re-deriving it case by case.Out of scope
kind:value. This is a doc-only PR.References
dependencies:↔breakingclarification).CLAUDE.md→ "Assigningkind:" (added in Schema: extend bin/validate-patterns to accept kind: field #54 / Accept optional kind: field in detection patterns (#54) #70).kind:field to detection patterns; renamebreaking_changes:→upgrade_findings:#53.