git 4.3.2 → 5.0.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/copilot-instructions.md +67 -2705
- data/.github/pull_request_template.md +3 -1
- data/.github/skills/breaking-change-analysis/SKILL.md +102 -0
- data/.github/skills/ci-cd-troubleshooting/SKILL.md +264 -0
- data/.github/skills/command-implementation/REFERENCE.md +993 -0
- data/.github/skills/command-implementation/SKILL.md +229 -0
- data/.github/skills/command-test-conventions/SKILL.md +660 -0
- data/.github/skills/command-yard-documentation/SKILL.md +426 -0
- data/.github/skills/dependency-management/SKILL.md +72 -0
- data/.github/skills/development-workflow/SKILL.md +506 -0
- data/.github/skills/extract-command-from-lib/SKILL.md +487 -0
- data/.github/skills/extract-facade-from-base-lib/SKILL.md +586 -0
- data/.github/skills/facade-implementation/REFERENCE.md +840 -0
- data/.github/skills/facade-implementation/SKILL.md +260 -0
- data/.github/skills/facade-test-conventions/SKILL.md +380 -0
- data/.github/skills/facade-yard-documentation/SKILL.md +429 -0
- data/.github/skills/make-skill-template/SKILL.md +176 -0
- data/.github/skills/pr-readiness-review/SKILL.md +185 -0
- data/.github/skills/project-context/SKILL.md +313 -0
- data/.github/skills/pull-request-review/SKILL.md +168 -0
- data/.github/skills/refactor-command-to-commandlineresult/SKILL.md +131 -0
- data/.github/skills/release-management/SKILL.md +125 -0
- data/.github/skills/review-arguments-dsl/CHECKLIST.md +788 -0
- data/.github/skills/review-arguments-dsl/SKILL.md +214 -0
- data/.github/skills/review-backward-compatibility/SKILL.md +275 -0
- data/.github/skills/review-cross-command-consistency/SKILL.md +139 -0
- data/.github/skills/reviewing-skills/SKILL.md +189 -0
- data/.github/skills/rspec-unit-testing-standards/SKILL.md +639 -0
- data/.github/skills/tdd-refactor-step/SKILL.md +236 -0
- data/.github/skills/test-debugging/SKILL.md +160 -0
- data/.github/skills/yard-documentation/SKILL.md +793 -0
- data/.github/workflows/continuous_integration.yml +3 -2
- data/.github/workflows/enforce_conventional_commits.yml +1 -1
- data/.github/workflows/experimental_continuous_integration.yml +2 -2
- data/.github/workflows/release.yml +3 -4
- data/.gitignore +8 -0
- data/.husky/pre-commit +13 -0
- data/.release-please-manifest.json +1 -1
- data/.rspec +3 -0
- data/.rubocop.yml +7 -3
- data/.rubocop_todo.yml +23 -5
- data/.yardopts +1 -0
- data/CHANGELOG.md +0 -40
- data/CONTRIBUTING.md +694 -53
- data/README.md +17 -5
- data/Rakefile +61 -9
- data/commitlint.test +4 -0
- data/git.gemspec +14 -8
- data/lib/git/args_builder.rb +0 -8
- data/lib/git/base.rb +486 -410
- data/lib/git/branch.rb +380 -43
- data/lib/git/branch_delete_failure.rb +31 -0
- data/lib/git/branch_delete_result.rb +63 -0
- data/lib/git/branch_info.rb +178 -0
- data/lib/git/branches.rb +130 -24
- data/lib/git/command_line/base.rb +245 -0
- data/lib/git/command_line/capturing.rb +249 -0
- data/lib/git/command_line/result.rb +96 -0
- data/lib/git/command_line/streaming.rb +194 -0
- data/lib/git/command_line.rb +43 -322
- data/lib/git/command_line_result.rb +4 -88
- data/lib/git/commands/add.rb +131 -0
- data/lib/git/commands/am/abort.rb +43 -0
- data/lib/git/commands/am/apply.rb +252 -0
- data/lib/git/commands/am/continue.rb +43 -0
- data/lib/git/commands/am/quit.rb +43 -0
- data/lib/git/commands/am/retry.rb +47 -0
- data/lib/git/commands/am/show_current_patch.rb +64 -0
- data/lib/git/commands/am/skip.rb +42 -0
- data/lib/git/commands/am.rb +33 -0
- data/lib/git/commands/apply.rb +237 -0
- data/lib/git/commands/archive/list_formats.rb +46 -0
- data/lib/git/commands/archive.rb +140 -0
- data/lib/git/commands/arguments.rb +3510 -0
- data/lib/git/commands/base.rb +403 -0
- data/lib/git/commands/branch/copy.rb +94 -0
- data/lib/git/commands/branch/create.rb +173 -0
- data/lib/git/commands/branch/delete.rb +80 -0
- data/lib/git/commands/branch/list.rb +162 -0
- data/lib/git/commands/branch/move.rb +94 -0
- data/lib/git/commands/branch/set_upstream.rb +86 -0
- data/lib/git/commands/branch/show_current.rb +49 -0
- data/lib/git/commands/branch/unset_upstream.rb +57 -0
- data/lib/git/commands/branch.rb +34 -0
- data/lib/git/commands/cat_file/batch.rb +364 -0
- data/lib/git/commands/cat_file/filtered.rb +105 -0
- data/lib/git/commands/cat_file/raw.rb +210 -0
- data/lib/git/commands/cat_file.rb +49 -0
- data/lib/git/commands/checkout/branch.rb +151 -0
- data/lib/git/commands/checkout/files.rb +115 -0
- data/lib/git/commands/checkout.rb +38 -0
- data/lib/git/commands/checkout_index.rb +105 -0
- data/lib/git/commands/clean.rb +100 -0
- data/lib/git/commands/clone.rb +240 -0
- data/lib/git/commands/commit.rb +272 -0
- data/lib/git/commands/commit_tree.rb +100 -0
- data/lib/git/commands/config_option_syntax/add.rb +83 -0
- data/lib/git/commands/config_option_syntax/get.rb +117 -0
- data/lib/git/commands/config_option_syntax/get_all.rb +115 -0
- data/lib/git/commands/config_option_syntax/get_color.rb +91 -0
- data/lib/git/commands/config_option_syntax/get_color_bool.rb +93 -0
- data/lib/git/commands/config_option_syntax/get_regexp.rb +115 -0
- data/lib/git/commands/config_option_syntax/get_urlmatch.rb +102 -0
- data/lib/git/commands/config_option_syntax/list.rb +107 -0
- data/lib/git/commands/config_option_syntax/remove_section.rb +74 -0
- data/lib/git/commands/config_option_syntax/rename_section.rb +78 -0
- data/lib/git/commands/config_option_syntax/replace_all.rb +104 -0
- data/lib/git/commands/config_option_syntax/set.rb +114 -0
- data/lib/git/commands/config_option_syntax/unset.rb +89 -0
- data/lib/git/commands/config_option_syntax/unset_all.rb +89 -0
- data/lib/git/commands/config_option_syntax.rb +56 -0
- data/lib/git/commands/describe.rb +155 -0
- data/lib/git/commands/diff.rb +656 -0
- data/lib/git/commands/diff_files.rb +518 -0
- data/lib/git/commands/diff_index.rb +496 -0
- data/lib/git/commands/fetch.rb +352 -0
- data/lib/git/commands/fsck.rb +136 -0
- data/lib/git/commands/gc.rb +132 -0
- data/lib/git/commands/grep.rb +338 -0
- data/lib/git/commands/init.rb +99 -0
- data/lib/git/commands/log.rb +632 -0
- data/lib/git/commands/ls_files.rb +191 -0
- data/lib/git/commands/ls_remote.rb +155 -0
- data/lib/git/commands/ls_tree.rb +131 -0
- data/lib/git/commands/maintenance/register.rb +75 -0
- data/lib/git/commands/maintenance/run.rb +104 -0
- data/lib/git/commands/maintenance/start.rb +66 -0
- data/lib/git/commands/maintenance/stop.rb +55 -0
- data/lib/git/commands/maintenance/unregister.rb +79 -0
- data/lib/git/commands/maintenance.rb +31 -0
- data/lib/git/commands/merge/abort.rb +44 -0
- data/lib/git/commands/merge/continue.rb +44 -0
- data/lib/git/commands/merge/quit.rb +46 -0
- data/lib/git/commands/merge/start.rb +245 -0
- data/lib/git/commands/merge.rb +28 -0
- data/lib/git/commands/merge_base.rb +86 -0
- data/lib/git/commands/mv.rb +77 -0
- data/lib/git/commands/name_rev.rb +114 -0
- data/lib/git/commands/pull.rb +377 -0
- data/lib/git/commands/push.rb +246 -0
- data/lib/git/commands/read_tree.rb +149 -0
- data/lib/git/commands/remote/add.rb +91 -0
- data/lib/git/commands/remote/get_url.rb +66 -0
- data/lib/git/commands/remote/list.rb +54 -0
- data/lib/git/commands/remote/prune.rb +61 -0
- data/lib/git/commands/remote/remove.rb +52 -0
- data/lib/git/commands/remote/rename.rb +69 -0
- data/lib/git/commands/remote/set_branches.rb +63 -0
- data/lib/git/commands/remote/set_head.rb +82 -0
- data/lib/git/commands/remote/set_url.rb +71 -0
- data/lib/git/commands/remote/set_url_add.rb +61 -0
- data/lib/git/commands/remote/set_url_delete.rb +64 -0
- data/lib/git/commands/remote/show.rb +71 -0
- data/lib/git/commands/remote/update.rb +72 -0
- data/lib/git/commands/remote.rb +42 -0
- data/lib/git/commands/repack.rb +277 -0
- data/lib/git/commands/reset.rb +147 -0
- data/lib/git/commands/rev_parse.rb +297 -0
- data/lib/git/commands/revert/abort.rb +45 -0
- data/lib/git/commands/revert/continue.rb +57 -0
- data/lib/git/commands/revert/quit.rb +47 -0
- data/lib/git/commands/revert/skip.rb +44 -0
- data/lib/git/commands/revert/start.rb +153 -0
- data/lib/git/commands/revert.rb +29 -0
- data/lib/git/commands/rm.rb +114 -0
- data/lib/git/commands/show.rb +632 -0
- data/lib/git/commands/show_ref/exclude_existing.rb +120 -0
- data/lib/git/commands/show_ref/exists.rb +78 -0
- data/lib/git/commands/show_ref/list.rb +145 -0
- data/lib/git/commands/show_ref/verify.rb +120 -0
- data/lib/git/commands/show_ref.rb +42 -0
- data/lib/git/commands/stash/apply.rb +75 -0
- data/lib/git/commands/stash/branch.rb +65 -0
- data/lib/git/commands/stash/clear.rb +41 -0
- data/lib/git/commands/stash/create.rb +58 -0
- data/lib/git/commands/stash/drop.rb +67 -0
- data/lib/git/commands/stash/list.rb +39 -0
- data/lib/git/commands/stash/pop.rb +78 -0
- data/lib/git/commands/stash/push.rb +103 -0
- data/lib/git/commands/stash/show.rb +149 -0
- data/lib/git/commands/stash/store.rb +63 -0
- data/lib/git/commands/stash.rb +38 -0
- data/lib/git/commands/status.rb +169 -0
- data/lib/git/commands/symbolic_ref/delete.rb +68 -0
- data/lib/git/commands/symbolic_ref/read.rb +95 -0
- data/lib/git/commands/symbolic_ref/update.rb +76 -0
- data/lib/git/commands/symbolic_ref.rb +38 -0
- data/lib/git/commands/tag/create.rb +139 -0
- data/lib/git/commands/tag/delete.rb +55 -0
- data/lib/git/commands/tag/list.rb +143 -0
- data/lib/git/commands/tag/verify.rb +71 -0
- data/lib/git/commands/tag.rb +26 -0
- data/lib/git/commands/update_ref/batch.rb +140 -0
- data/lib/git/commands/update_ref/delete.rb +92 -0
- data/lib/git/commands/update_ref/update.rb +106 -0
- data/lib/git/commands/update_ref.rb +42 -0
- data/lib/git/commands/version.rb +52 -0
- data/lib/git/commands/worktree/add.rb +140 -0
- data/lib/git/commands/worktree/list.rb +64 -0
- data/lib/git/commands/worktree/lock.rb +58 -0
- data/lib/git/commands/worktree/management_base.rb +51 -0
- data/lib/git/commands/worktree/move.rb +66 -0
- data/lib/git/commands/worktree/prune.rb +67 -0
- data/lib/git/commands/worktree/remove.rb +63 -0
- data/lib/git/commands/worktree/repair.rb +76 -0
- data/lib/git/commands/worktree/unlock.rb +47 -0
- data/lib/git/commands/worktree.rb +43 -0
- data/lib/git/commands/write_tree.rb +68 -0
- data/lib/git/commands.rb +89 -0
- data/lib/git/detached_head_info.rb +54 -0
- data/lib/git/diff.rb +297 -7
- data/lib/git/diff_file_numstat_info.rb +29 -0
- data/lib/git/diff_file_patch_info.rb +134 -0
- data/lib/git/diff_file_raw_info.rb +127 -0
- data/lib/git/diff_info.rb +169 -0
- data/lib/git/diff_path_status.rb +78 -19
- data/lib/git/diff_result.rb +32 -0
- data/lib/git/diff_stats.rb +59 -14
- data/lib/git/dirstat_info.rb +86 -0
- data/lib/git/errors.rb +65 -2
- data/lib/git/execution_context/global.rb +56 -0
- data/lib/git/execution_context/repository.rb +147 -0
- data/lib/git/execution_context.rb +482 -0
- data/lib/git/file_ref.rb +74 -0
- data/lib/git/fsck_object.rb +9 -9
- data/lib/git/fsck_result.rb +1 -1
- data/lib/git/lib.rb +1606 -1028
- data/lib/git/log.rb +15 -2
- data/lib/git/object.rb +92 -22
- data/lib/git/parsers/branch.rb +224 -0
- data/lib/git/parsers/cat_file.rb +111 -0
- data/lib/git/parsers/diff.rb +585 -0
- data/lib/git/parsers/fsck.rb +133 -0
- data/lib/git/parsers/grep.rb +42 -0
- data/lib/git/parsers/ls_tree.rb +58 -0
- data/lib/git/parsers/stash.rb +208 -0
- data/lib/git/parsers/tag.rb +257 -0
- data/lib/git/remote.rb +133 -9
- data/lib/git/repository/branching.rb +572 -0
- data/lib/git/repository/committing.rb +191 -0
- data/lib/git/repository/configuring.rb +156 -0
- data/lib/git/repository/diffing.rb +775 -0
- data/lib/git/repository/inspecting.rb +153 -0
- data/lib/git/repository/logging.rb +247 -0
- data/lib/git/repository/merging.rb +295 -0
- data/lib/git/repository/object_operations.rb +1101 -0
- data/lib/git/repository/path_resolver.rb +207 -0
- data/lib/git/repository/remote_operations.rb +753 -0
- data/lib/git/repository/shared_private.rb +51 -0
- data/lib/git/repository/staging.rb +390 -0
- data/lib/git/repository/stashing.rb +107 -0
- data/lib/git/repository/status_operations.rb +180 -0
- data/lib/git/repository/worktree_operations.rb +159 -0
- data/lib/git/repository.rb +264 -1
- data/lib/git/stash.rb +85 -4
- data/lib/git/stash_info.rb +104 -0
- data/lib/git/stashes.rb +130 -13
- data/lib/git/status.rb +224 -18
- data/lib/git/tag_delete_failure.rb +31 -0
- data/lib/git/tag_delete_result.rb +63 -0
- data/lib/git/tag_info.rb +105 -0
- data/lib/git/version.rb +109 -2
- data/lib/git/version_constraint.rb +81 -0
- data/lib/git/worktree.rb +120 -5
- data/lib/git/worktrees.rb +107 -7
- data/lib/git.rb +114 -18
- data/redesign/1_architecture_existing.md +54 -18
- data/redesign/2_architecture_redesign.md +365 -46
- data/redesign/3_architecture_implementation.md +1451 -54
- data/tasks/gem_tasks.rake +4 -0
- data/tasks/npm_tasks.rake +7 -0
- data/tasks/rspec.rake +48 -0
- data/tasks/test.rake +13 -1
- data/tasks/yard.rake +34 -7
- metadata +349 -20
- data/lib/git/index.rb +0 -6
- data/lib/git/path.rb +0 -38
- data/lib/git/working_directory.rb +0 -6
- /data/{release-please-config.json → .release-please-config.json} +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd-refactor-step
|
|
3
|
+
description: 'Guides the REFACTOR step of the TDD cycle — identifying code smells, applying safe refactoring techniques, cleaning test code, and verifying with rubocop. Use during RED-GREEN-REFACTOR when deciding what and how to refactor after making a test pass.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TDD Refactor Step
|
|
7
|
+
|
|
8
|
+
Concrete guidance for the REFACTOR step of the RED-GREEN-REFACTOR cycle. Covers
|
|
9
|
+
code smells to look for, safe refactoring techniques, test code cleanup,
|
|
10
|
+
rubocop integration, and verification.
|
|
11
|
+
|
|
12
|
+
## Contents
|
|
13
|
+
|
|
14
|
+
- [How to use this skill](#how-to-use-this-skill)
|
|
15
|
+
- [Related skills](#related-skills)
|
|
16
|
+
- [Decision: refactor or skip](#decision-refactor-or-skip)
|
|
17
|
+
- [Code smells checklist](#code-smells-checklist)
|
|
18
|
+
- [Refactoring techniques](#refactoring-techniques)
|
|
19
|
+
- [Test code refactoring](#test-code-refactoring)
|
|
20
|
+
- [Rubocop integration](#rubocop-integration)
|
|
21
|
+
- [Verification](#verification)
|
|
22
|
+
- [Project-specific patterns](#project-specific-patterns)
|
|
23
|
+
- [Boundaries](#boundaries)
|
|
24
|
+
|
|
25
|
+
## How to use this skill
|
|
26
|
+
|
|
27
|
+
Invoke this skill during the REFACTOR step of the TDD cycle, after GREEN
|
|
28
|
+
(all tests pass). This skill expands the guidance in the
|
|
29
|
+
[Development Workflow](../development-workflow/SKILL.md) REFACTOR step.
|
|
30
|
+
|
|
31
|
+
Typical invocation:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
I just got to green. Walk me through the REFACTOR step using
|
|
35
|
+
the TDD Refactor Step skill.
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Related skills
|
|
39
|
+
|
|
40
|
+
- [Development Workflow](../development-workflow/SKILL.md) — parent TDD workflow;
|
|
41
|
+
this skill supplements its REFACTOR step
|
|
42
|
+
- [RSpec Unit Testing Standards](../rspec-unit-testing-standards/SKILL.md) — test
|
|
43
|
+
conventions that refactored tests must follow
|
|
44
|
+
- [PR Readiness Review](../pr-readiness-review/SKILL.md) — final quality gate
|
|
45
|
+
that catches remaining quality issues
|
|
46
|
+
|
|
47
|
+
## Decision: refactor or skip
|
|
48
|
+
|
|
49
|
+
Not every GREEN step needs refactoring. Skip when **all** of the following are true:
|
|
50
|
+
|
|
51
|
+
- No hardcoded values from the GREEN step remain
|
|
52
|
+
- No duplication was introduced between new and existing code
|
|
53
|
+
- No method exceeds ~10 lines
|
|
54
|
+
- No parameter list exceeds 3 positional parameters
|
|
55
|
+
- Rubocop reports no new offenses on changed files
|
|
56
|
+
- Test setup is not duplicated across examples
|
|
57
|
+
|
|
58
|
+
If any condition is false, proceed with the relevant technique below.
|
|
59
|
+
|
|
60
|
+
## Code smells checklist
|
|
61
|
+
|
|
62
|
+
Check the code written or modified in this task for these smells, in priority order:
|
|
63
|
+
|
|
64
|
+
| # | Smell | Threshold | Action |
|
|
65
|
+
| --- | ----- | --------- | ------ |
|
|
66
|
+
| 1 | **Hardcoded values** from GREEN step | Any | Generalize to actual logic |
|
|
67
|
+
| 2 | **Duplication** between new code and existing code | ≥ 3 similar lines | Extract shared method or constant |
|
|
68
|
+
| 3 | **Long method** | > 10 lines (body) | Extract private helper |
|
|
69
|
+
| 4 | **Long parameter list** | > 3 positional params | Convert trailing params to keyword arguments |
|
|
70
|
+
| 5 | **Inconsistent naming** | Deviates from file/module conventions | Rename to match existing patterns |
|
|
71
|
+
| 6 | **Deeply nested conditionals** | > 2 levels | Extract guard clause or helper |
|
|
72
|
+
| 7 | **Feature envy** | Method uses another object's data more than its own | Move method or extract delegator |
|
|
73
|
+
| 8 | **Dead code** | Unreachable branches, unused variables | Remove |
|
|
74
|
+
|
|
75
|
+
Limit yourself to smells **in files you touched this task**. Broader cleanup belongs
|
|
76
|
+
in a separate task (add it during REPLAN).
|
|
77
|
+
|
|
78
|
+
## Refactoring techniques
|
|
79
|
+
|
|
80
|
+
Apply the simplest technique that resolves the smell:
|
|
81
|
+
|
|
82
|
+
### Extract method
|
|
83
|
+
|
|
84
|
+
Split a long method into a public method and one or more private helpers. Name
|
|
85
|
+
the helper after **what** it does, not **how**:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
# Before
|
|
89
|
+
def call(*, **)
|
|
90
|
+
bound = args_definition.bind(*, **)
|
|
91
|
+
objects = Array(bound.objects).map { |o| "#{o}\n" }.join
|
|
92
|
+
with_stdin(objects) { |r| run_batch(bound, r) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# After
|
|
96
|
+
def call(*, **)
|
|
97
|
+
bound = args_definition.bind(*, **)
|
|
98
|
+
with_stdin(stdin_content(bound)) { |r| run_batch(bound, r) }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def stdin_content(bound)
|
|
104
|
+
Array(bound.objects).map { |o| "#{o}\n" }.join
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Convert positional to keyword arguments
|
|
109
|
+
|
|
110
|
+
When a method accumulates optional trailing positional parameters:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# Before
|
|
114
|
+
def initialize(args, options, positionals, exec_names = [], flags = [])
|
|
115
|
+
|
|
116
|
+
# After
|
|
117
|
+
def initialize(args, options, positionals, exec_names: [], flags: [])
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Update the single call site at the same time.
|
|
121
|
+
|
|
122
|
+
### Replace conditional with guard clause
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
# Before
|
|
126
|
+
def validate(value)
|
|
127
|
+
if value
|
|
128
|
+
if value.is_a?(String)
|
|
129
|
+
process(value)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# After
|
|
135
|
+
def validate(value)
|
|
136
|
+
return unless value
|
|
137
|
+
return unless value.is_a?(String)
|
|
138
|
+
|
|
139
|
+
process(value)
|
|
140
|
+
end
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Introduce constant
|
|
144
|
+
|
|
145
|
+
When a magic value appears in logic:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
# Before
|
|
149
|
+
raise error if version < Git::Version.parse('2.28.0')
|
|
150
|
+
|
|
151
|
+
# After
|
|
152
|
+
MINIMUM_GIT_VERSION = Git::Version.parse('2.28.0')
|
|
153
|
+
raise error if version < MINIMUM_GIT_VERSION
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Eliminate duplication with shared setup
|
|
157
|
+
|
|
158
|
+
When two methods share identical preamble or teardown, extract the shared part.
|
|
159
|
+
If the duplication is in tests, see Test Code Refactoring below.
|
|
160
|
+
|
|
161
|
+
## Test code refactoring
|
|
162
|
+
|
|
163
|
+
Test code deserves the same refactoring attention as production code:
|
|
164
|
+
|
|
165
|
+
| Smell | Technique |
|
|
166
|
+
| ----- | --------- |
|
|
167
|
+
| Duplicated `let`/`before` across contexts | Move to nearest shared `describe` or `context` |
|
|
168
|
+
| Long example bodies (> 5 lines of setup) | Extract to `let` declarations or `before` block |
|
|
169
|
+
| Repeated literal values | Extract to `let` or constant at top of file |
|
|
170
|
+
| Identical examples across files | Extract to shared example group (`shared_examples`) |
|
|
171
|
+
| Unclear example descriptions | Rewrite to state expected behavior, not implementation |
|
|
172
|
+
|
|
173
|
+
Follow the [RSpec Unit Testing Standards](../rspec-unit-testing-standards/SKILL.md)
|
|
174
|
+
for the resulting test structure.
|
|
175
|
+
|
|
176
|
+
## Rubocop integration
|
|
177
|
+
|
|
178
|
+
Run rubocop on changed files after refactoring:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
bundle exec rubocop $(git diff --name-only HEAD)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Focus on:
|
|
185
|
+
- `Metrics/MethodLength` — methods over the configured limit
|
|
186
|
+
- `Metrics/ParameterLists` — too many parameters
|
|
187
|
+
- `Metrics/AbcSize` — complexity threshold
|
|
188
|
+
- `Style` cops — naming, formatting consistency
|
|
189
|
+
|
|
190
|
+
Auto-correct safe offenses when appropriate:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
bundle exec rubocop -a $(git diff --name-only HEAD)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Do not auto-correct `Metrics` cops — those require structural changes (extract
|
|
197
|
+
method, split class), not formatting fixes.
|
|
198
|
+
|
|
199
|
+
## Verification
|
|
200
|
+
|
|
201
|
+
After refactoring, confirm:
|
|
202
|
+
|
|
203
|
+
1. **Tests still pass:** `bundle exec rspec <spec_file>` for the current task's
|
|
204
|
+
test file(s)
|
|
205
|
+
2. **No new rubocop offenses:** `bundle exec rubocop $(git diff --name-only HEAD)`
|
|
206
|
+
3. **Behavior unchanged:** No new test was added during REFACTOR — if you need
|
|
207
|
+
a new test, you skipped a RED step
|
|
208
|
+
|
|
209
|
+
If any test fails after refactoring, the refactoring changed behavior. Revert
|
|
210
|
+
and try a smaller change.
|
|
211
|
+
|
|
212
|
+
## Project-specific patterns
|
|
213
|
+
|
|
214
|
+
Patterns specific to this codebase that the REFACTOR step should enforce:
|
|
215
|
+
|
|
216
|
+
- **Command classes** should not contain parsing logic — if refactoring reveals
|
|
217
|
+
parsing in a command class, flag it for extraction (separate task)
|
|
218
|
+
- **Arguments DSL** `Bound` metadata uses keyword arguments — if adding new
|
|
219
|
+
metadata fields, follow the keyword argument pattern
|
|
220
|
+
- **Error classes** inherit from `Git::Error` — ensure new errors follow the
|
|
221
|
+
hierarchy in `lib/git/errors.rb`
|
|
222
|
+
- **`freeze` constants** — all new constants should be frozen
|
|
223
|
+
(`CONSTANT = value.freeze`)
|
|
224
|
+
- **Private methods** go below a single `private` keyword, not inline
|
|
225
|
+
`private def`
|
|
226
|
+
|
|
227
|
+
## Boundaries
|
|
228
|
+
|
|
229
|
+
Things the REFACTOR step must **not** do:
|
|
230
|
+
|
|
231
|
+
- **Add new behavior** — no new features, no new test cases
|
|
232
|
+
- **Change public API** — method signatures visible to users stay the same
|
|
233
|
+
- **Touch unrelated files** — scope to files modified in this task; add broader
|
|
234
|
+
refactoring to the task list during REPLAN
|
|
235
|
+
- **Optimize prematurely** — clarity over performance unless profiling data exists
|
|
236
|
+
- **Over-abstract** — do not create a helper for something used exactly once
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-debugging
|
|
3
|
+
description: "Debugs failing or flaky tests and improves test coverage. Use when tests fail consistently, exhibit intermittent behavior, or when adding missing test coverage."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test Debugging & Maintenance Workflow
|
|
7
|
+
|
|
8
|
+
When asked to debug tests or improve test coverage, follow this workflow to identify
|
|
9
|
+
problems, determine root causes, and apply appropriate fixes.
|
|
10
|
+
|
|
11
|
+
## Contents
|
|
12
|
+
|
|
13
|
+
- [How to use this skill](#how-to-use-this-skill)
|
|
14
|
+
- [Related skills](#related-skills)
|
|
15
|
+
- [Step 1: Run and Observe the Test](#step-1-run-and-observe-the-test)
|
|
16
|
+
- [Step 2: Investigate Root Cause](#step-2-investigate-root-cause)
|
|
17
|
+
- [Step 3: Report Findings](#step-3-report-findings)
|
|
18
|
+
- [Step 4: Determine Fix Strategy](#step-4-determine-fix-strategy)
|
|
19
|
+
- [Step 5: Verify Test Fix](#step-5-verify-test-fix)
|
|
20
|
+
- [Project-Specific Considerations](#project-specific-considerations)
|
|
21
|
+
|
|
22
|
+
## How to use this skill
|
|
23
|
+
|
|
24
|
+
Attach this file to your Copilot Chat context, then invoke it with the failing
|
|
25
|
+
test file, test name, or flakiness symptom. Stop at Step 3 for diagnosis-only
|
|
26
|
+
requests unless the user asks for implementation.
|
|
27
|
+
|
|
28
|
+
## Related skills
|
|
29
|
+
|
|
30
|
+
- [RSpec Unit Testing Standards](../rspec-unit-testing-standards/SKILL.md) — rules
|
|
31
|
+
governing test isolation, determinism, and order independence (Rules 25–28); consult
|
|
32
|
+
when flakiness is caused by a test design violation rather than a production bug
|
|
33
|
+
- [Development Workflow](../development-workflow/SKILL.md) — required TDD process
|
|
34
|
+
when fixes involve production code
|
|
35
|
+
- [CI/CD Troubleshooting](../ci-cd-troubleshooting/SKILL.md) — investigate
|
|
36
|
+
failures that appear only in CI
|
|
37
|
+
|
|
38
|
+
## Step 1: Run and Observe the Test
|
|
39
|
+
|
|
40
|
+
1. **Run the failing test:**
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# TestUnit (legacy tests in tests/units/)
|
|
44
|
+
bundle exec bin/test <test_file_name>
|
|
45
|
+
|
|
46
|
+
# RSpec unit test
|
|
47
|
+
bundle exec rspec spec/unit/git/commands/<command>_spec.rb
|
|
48
|
+
|
|
49
|
+
# RSpec integration test
|
|
50
|
+
bundle exec rspec spec/integration/git/commands/<command>_spec.rb
|
|
51
|
+
|
|
52
|
+
# Specific test method (TestUnit)
|
|
53
|
+
bundle exec ruby -I lib:tests tests/units/test_base.rb -n test_method_name
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
2. **For suspected flaky tests, run multiple times:**
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
for i in {1..20}; do
|
|
60
|
+
echo "Run $i"
|
|
61
|
+
bundle exec bin/test <test_file> || break
|
|
62
|
+
done
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
3. **Check test isolation** — run the test alone vs. within the full suite:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bundle exec bin/test <test_file> # alone
|
|
69
|
+
bundle exec rake default # full suite
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Step 2: Investigate Root Cause
|
|
73
|
+
|
|
74
|
+
1. **Read the full error** including stack trace. Identify exact failing line and
|
|
75
|
+
expected vs. actual values.
|
|
76
|
+
|
|
77
|
+
2. **Check recent changes** with `git log` and `git blame` on the test file and
|
|
78
|
+
related production code.
|
|
79
|
+
|
|
80
|
+
3. **For flaky tests**, look for:
|
|
81
|
+
- Shared state between tests (global/class variables, shared filesystem resources)
|
|
82
|
+
- Timing dependencies or race conditions
|
|
83
|
+
- Non-deterministic behavior (time-dependent logic, unordered iteration)
|
|
84
|
+
- Test execution order dependencies
|
|
85
|
+
|
|
86
|
+
4. **For environment issues**, check:
|
|
87
|
+
- Platform differences (paths, line endings, permissions)
|
|
88
|
+
- Git version differences (use `git --version`)
|
|
89
|
+
- Ruby version differences
|
|
90
|
+
|
|
91
|
+
## Step 3: Report Findings
|
|
92
|
+
|
|
93
|
+
Present diagnostic findings to the user:
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
# Test Failure Diagnosis: <test_name>
|
|
97
|
+
|
|
98
|
+
**Failure Type:** [Consistent / Flaky / Coverage Gap]
|
|
99
|
+
**Test File:** <path/to/test_file.rb>
|
|
100
|
+
|
|
101
|
+
## Error
|
|
102
|
+
<error message and relevant stack trace>
|
|
103
|
+
|
|
104
|
+
## Root Cause
|
|
105
|
+
<Explanation of why the test is failing>
|
|
106
|
+
|
|
107
|
+
## Recommended Fix
|
|
108
|
+
<Specific recommendation>
|
|
109
|
+
|
|
110
|
+
**Would you like me to implement this fix?**
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**STOP here** unless the user asks you to proceed with the fix.
|
|
114
|
+
|
|
115
|
+
## Step 4: Determine Fix Strategy
|
|
116
|
+
|
|
117
|
+
| Scenario | Strategy | Commit Type |
|
|
118
|
+
| --- | --- | --- |
|
|
119
|
+
| **Production code bug** (test caught a real bug) | Fix production code using the development-workflow TDD process. The failing test is the RED step. | `fix(component): <description>` |
|
|
120
|
+
| **Test needs updating** (intentional API change) | Get user confirmation first. Update test assertions. | `test(component): update test for <change>` |
|
|
121
|
+
| **Flaky test** (non-determinism) | Make test deterministic. Run 20+ times to verify. | `test(component): fix flaky test in <test_name>` |
|
|
122
|
+
| **Missing test coverage** | Add tests using the development-workflow TDD process. | `test(component): add tests for <feature>` |
|
|
123
|
+
| **Test refactoring** | Improve readability/reduce duplication. Keep tests green. | `refactor(test): improve <test_name>` |
|
|
124
|
+
| **Environment/setup issue** | Fix environment, document requirements. No code commit needed. | — |
|
|
125
|
+
|
|
126
|
+
**CRITICAL:** Get user confirmation before modifying existing tests.
|
|
127
|
+
|
|
128
|
+
## Step 5: Verify Test Fix
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Run the specific test
|
|
132
|
+
bundle exec bin/test <test_file> # TestUnit
|
|
133
|
+
bundle exec rspec <spec_file> # RSpec
|
|
134
|
+
|
|
135
|
+
# For flaky test fixes, run many times
|
|
136
|
+
for i in {1..50}; do
|
|
137
|
+
echo "Run $i"
|
|
138
|
+
bundle exec bin/test <test_file> || break
|
|
139
|
+
done
|
|
140
|
+
|
|
141
|
+
# Run full suite
|
|
142
|
+
bundle exec rake default
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Project-Specific Considerations
|
|
146
|
+
|
|
147
|
+
**Test frameworks:** This project uses both TestUnit (legacy tests in `tests/units/`)
|
|
148
|
+
and RSpec (new tests in `spec/`). Run both when verifying changes.
|
|
149
|
+
|
|
150
|
+
**Test helpers:** Use `clone_working_repo`, `create_temp_repo`, `in_temp_dir` from
|
|
151
|
+
test helpers for TestUnit. Use `include_context` shared contexts for RSpec.
|
|
152
|
+
|
|
153
|
+
**Mocking:** The project uses Mocha for TestUnit mocking and RSpec doubles for RSpec.
|
|
154
|
+
Be careful with stubs — they can mask real issues.
|
|
155
|
+
|
|
156
|
+
**Test data:** Fixtures are in `tests/files/`. Use test helpers to create temporary
|
|
157
|
+
repos. Clean up in teardown.
|
|
158
|
+
|
|
159
|
+
**CI vs. local differences:** If tests pass locally but fail in CI, use the
|
|
160
|
+
ci-cd-troubleshooting skill.
|