Skip to content

Commit dcc5277

Browse files
committed
ci: fix ci
1 parent 4bef09d commit dcc5277

File tree

9 files changed

+66
-358
lines changed

9 files changed

+66
-358
lines changed

.cursor/commands/batch-problem-creation.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,8 @@ Execute complete workflow from `.cursor/commands/problem-creation.md`:
5353

5454
**CRITICAL**: Before running quality assurance, implement the optimal solution:
5555

56-
1. **Implement solution**: Write the optimal algorithm in `solution.py`
57-
2. **Add multiple approaches**: Include alternative solutions (e.g., Solution, SolutionOptimized)
58-
3. **Use parametrized testing**: Ensure all solution approaches are tested
59-
4. **Verify correctness**: Solution must handle all test cases correctly
56+
1. **Implement solution**: Write the optimal algorithm in `solution.py` - implement only 1 solution in the `Solution` class, no need to add more classes
57+
2. **Verify correctness**: Solution must handle all test cases correctly
6058

6159
#### 2.4: Quality Assurance & Reproducibility Verification
6260

.cursor/commands/test-quality-assurance.md

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,51 @@
11
# Test Quality Assurance Rules
22

3-
## Simple Enhancement Workflow
4-
5-
When user requests test case enhancement or **test reproducibility verification**:
3+
## CRITICAL: Follow These Steps EXACTLY - No Deviations
64

75
### 1. Problem Resolution
86

97
- Use active file context or user-provided problem name
108
- If unclear, run: `poetry run python -m leetcode_py.tools.check_test_cases --threshold=10 --max=1`
119

12-
### 2. Enhancement Process
10+
### 2. Test Reproducibility Verification Process
11+
12+
**MANDATORY 6-Step Process - Execute in Order:**
1313

1414
```bash
15-
# Simple 4-step process:
16-
# 1. Update JSON template with more test cases (12-15 total)
17-
# 2. Backup original
18-
mv leetcode/{problem_name} .cache/leetcode/{problem_name}
19-
# 3. Regenerate with enhanced tests
20-
make p-gen PROBLEM={problem_name} FORCE=1 && make p-lint PROBLEM={problem_name}
21-
# 4. Restore original solution, keep enhanced tests
22-
cp .cache/leetcode/{problem_name}/solution.py leetcode/{problem_name}/solution.py
23-
```
15+
# Step 1: Backup original files
16+
cp -r leetcode/{problem_name} leetcode/{problem_name}_backup
2417

25-
### 3. Verification
18+
# Step 2: Regenerate from JSON template (use Makefile, NOT poetry run)
19+
make p-gen PROBLEM={problem_name} FORCE=1
2620

27-
- Run `make p-test PROBLEM={problem_name}`
28-
- Fix any incorrect expected values in test cases
29-
- Update JSON template with corrections
21+
# Step 3: Restore original solution ONLY
22+
cp leetcode/{problem_name}_backup/solution.py leetcode/{problem_name}/solution.py
3023

31-
### 4. Restore Backup
24+
# Step 4: Verify mypy passes (CRITICAL for CI)
25+
poetry run mypy leetcode/{problem_name}/test_solution.py --explicit-package-bases --install-types --non-interactive --check-untyped-defs
3226

33-
```bash
34-
# Copy enhanced test_solution.py to backup
35-
cp leetcode/{problem_name}/test_solution.py .cache/leetcode/{problem_name}/
36-
# Restore all original files (preserves user edits)
37-
rm -rf leetcode/{problem_name}
38-
mv .cache/leetcode/{problem_name} leetcode/{problem_name}
27+
# Step 5: Verify tests pass (expected to fail if solution is incomplete)
28+
make p-test PROBLEM={problem_name}
29+
30+
# Step 6: Cleanup
31+
rm -rf leetcode/{problem_name}_backup
3932
```
4033

34+
### 3. What NOT to Do
35+
36+
-**NEVER edit cookiecutter templates** (`{{cookiecutter.problem_name}}/` files)
37+
-**NEVER use `poetry run python -m leetcode_py.cli.main gen`** - use `make p-gen` instead
38+
-**NEVER modify helpers.py manually** - let regeneration handle it
39+
-**NEVER skip mypy verification** - this is the main CI issue
40+
-**NEVER assume tests will pass** - they may fail if solution is incomplete
41+
42+
### 4. What to Do
43+
44+
-**ALWAYS use `make p-gen PROBLEM={problem_name} FORCE=1`** for regeneration
45+
-**ALWAYS verify mypy passes** before considering task complete
46+
-**ALWAYS restore original solution** after regeneration
47+
-**ALWAYS check JSON template** if mypy fails (look for `assert_assert_` bugs)
48+
4149
## Test Case Standards
4250

4351
### Coverage Requirements
@@ -84,24 +92,34 @@ poetry run python -m leetcode_py.tools.check_test_cases --threshold=12
8492
make p-gen PROBLEM={problem_name} FORCE=1
8593
```
8694

87-
## Test Reproducibility Verification
95+
## Common Issues & Solutions
8896

89-
Use this same workflow when CI tests fail due to reproducibility issues:
97+
### Issue: `assert_assert_missing_number` Error
9098

91-
**Process Name**: Test Reproducibility Verification
99+
**Cause**: JSON template has `helpers_assert_name: "assert_missing_number"` but template adds `assert_` prefix
100+
**Solution**: Change JSON to `helpers_assert_name: "missing_number"` so template generates `assert_missing_number`
92101

93-
**When to Use**:
102+
### Issue: mypy Import Errors
94103

95-
- CI test failures in reproducibility checks
96-
- Inconsistent test results between environments
97-
- Missing edge cases causing coverage gaps
98-
- Need to ensure 100% code coverage
104+
**Cause**: Regenerated helpers.py doesn't match test imports
105+
**Solution**: Use `make p-gen` (not poetry run) and verify JSON template is correct
106+
107+
### Issue: Tests Fail After Regeneration
108+
109+
**Expected**: Tests may fail if solution is incomplete (returns 0 or placeholder)
110+
**Action**: This is normal - focus on mypy passing, not test results
99111

100112
## Success Criteria
101113

102-
- All tests pass with enhanced test cases
103-
- Minimum 12 comprehensive test cases per problem
104-
- Original solution code preserved
105-
- **Enhanced test cases in final test_solution.py**
106-
- JSON template updated for future regeneration
107-
- **100% code coverage including edge cases**
114+
-**mypy passes** with no errors (CRITICAL for CI)
115+
-**Test structure matches JSON template** exactly
116+
-**Original solution preserved** (user's code intact)
117+
-**helpers.py generated correctly** (no `assert_assert_` bugs)
118+
-**Reproducibility verified** (can regenerate consistently)
119+
120+
## When to Use This Workflow
121+
122+
- GitHub Actions CI failures due to mypy errors
123+
- Test reproducibility verification requests
124+
- Need to ensure test structure matches JSON template
125+
- CI test failures in reproducibility checks

leetcode/counting_bits/solution.py

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,6 @@
22

33

44
class Solution:
5-
def count_bits(self, n: int) -> List[int]:
6-
"""
7-
Count bits using dynamic programming approach.
8-
9-
Key insight: For any number i, the number of 1s in its binary representation
10-
is equal to the number of 1s in i >> 1 (right shift by 1) plus the least significant bit.
11-
12-
Time: O(n)
13-
Space: O(1) excluding output array
14-
"""
15-
result = [0] * (n + 1)
16-
17-
for i in range(1, n + 1):
18-
# result[i] = result[i >> 1] + (i & 1)
19-
# i >> 1 removes the least significant bit
20-
# i & 1 gets the least significant bit
21-
result[i] = result[i >> 1] + (i & 1)
22-
23-
return result
24-
25-
26-
class SolutionOptimized:
275
def count_bits(self, n: int) -> List[int]:
286
"""
297
Optimized version with better variable naming and comments.
@@ -42,38 +20,3 @@ def count_bits(self, n: int) -> List[int]:
4220
bits_count[num] = bits_count[num >> 1] + (num & 1)
4321

4422
return bits_count
45-
46-
47-
class SolutionNaive:
48-
def count_bits(self, n: int) -> List[int]:
49-
"""
50-
Naive approach using built-in bin() function.
51-
52-
Time: O(n log n) - bin() takes O(log n) time
53-
Space: O(1) excluding output array
54-
"""
55-
result = []
56-
for i in range(n + 1):
57-
result.append(bin(i).count("1"))
58-
return result
59-
60-
61-
class SolutionBitManipulation:
62-
def count_bits(self, n: int) -> List[int]:
63-
"""
64-
Using bit manipulation to count 1s directly.
65-
66-
Time: O(n log n) - each number takes O(log n) time
67-
Space: O(1) excluding output array
68-
"""
69-
result = []
70-
71-
for i in range(n + 1):
72-
count = 0
73-
num = i
74-
while num > 0:
75-
count += num & 1 # Check if least significant bit is 1
76-
num >>= 1 # Right shift to check next bit
77-
result.append(count)
78-
79-
return result

leetcode/house_robber_ii/solution.py

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,6 @@
22

33

44
class Solution:
5-
def rob(self, nums: List[int]) -> int:
6-
"""
7-
House Robber II - Circular arrangement.
8-
9-
The key insight is that since houses are in a circle, we can't rob both
10-
the first and last house. So we have two cases:
11-
1. Rob houses 0 to n-2 (exclude last house)
12-
2. Rob houses 1 to n-1 (exclude first house)
13-
14-
Time: O(n)
15-
Space: O(1)
16-
"""
17-
if not nums:
18-
return 0
19-
if len(nums) == 1:
20-
return nums[0]
21-
if len(nums) == 2:
22-
return max(nums[0], nums[1])
23-
24-
def rob_linear(houses: List[int]) -> int:
25-
"""Rob houses in linear arrangement (no circle)."""
26-
prev2 = prev1 = 0
27-
for money in houses:
28-
current = max(prev1, prev2 + money)
29-
prev2, prev1 = prev1, current
30-
return prev1
31-
32-
# Case 1: Rob houses 0 to n-2 (exclude last house)
33-
case1 = rob_linear(nums[:-1])
34-
35-
# Case 2: Rob houses 1 to n-1 (exclude first house)
36-
case2 = rob_linear(nums[1:])
37-
38-
return max(case1, case2)
39-
40-
41-
class SolutionOptimized:
425
def rob(self, nums: List[int]) -> int:
436
"""
447
Optimized version with better variable naming and edge case handling.

leetcode/missing_number/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**Difficulty:** Easy
44
**Topics:** Array, Hash Table, Math, Binary Search, Bit Manipulation, Sorting
5-
**Tags:** blind-75
5+
**Tags:** neetcode-150, blind-75
66

77
**LeetCode:** [Problem 268](https://leetcode.com/problems/missing-number/description/)
88

Lines changed: 5 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,7 @@
1-
from typing import List
2-
3-
41
class Solution:
5-
def missing_number(self, nums: List[int]) -> int:
6-
"""
7-
Find the missing number using mathematical approach.
8-
9-
The sum of numbers from 0 to n is n*(n+1)/2.
10-
The missing number is the difference between expected sum and actual sum.
11-
12-
Time: O(n)
13-
Space: O(1)
14-
"""
15-
n = len(nums)
16-
expected_sum = n * (n + 1) // 2
17-
actual_sum = sum(nums)
18-
return expected_sum - actual_sum
19-
20-
21-
class SolutionOptimized:
22-
def missing_number(self, nums: List[int]) -> int:
23-
"""
24-
Find the missing number using XOR approach.
25-
26-
XOR has the property that a ^ a = 0 and a ^ 0 = a.
27-
If we XOR all numbers from 0 to n with all numbers in nums,
28-
the missing number will be the only one left.
29-
30-
Time: O(n)
31-
Space: O(1)
32-
"""
33-
n = len(nums)
34-
result = n # Start with n since we're missing one number
35-
36-
for i in range(n):
37-
result ^= i ^ nums[i]
38-
39-
return result
40-
41-
42-
class SolutionHashSet:
43-
def missing_number(self, nums: List[int]) -> int:
44-
"""
45-
Find the missing number using hash set approach.
46-
47-
Time: O(n)
48-
Space: O(n)
49-
"""
50-
num_set = set(nums)
51-
n = len(nums)
52-
53-
for i in range(n + 1):
54-
if i not in num_set:
55-
return i
56-
57-
return -1 # Should never reach here
58-
59-
60-
class SolutionSorting:
61-
def missing_number(self, nums: List[int]) -> int:
62-
"""
63-
Find the missing number using sorting approach.
64-
65-
Time: O(n log n)
66-
Space: O(1) if sorting in-place
67-
"""
68-
nums.sort()
69-
n = len(nums)
70-
71-
# Check if 0 is missing
72-
if nums[0] != 0:
73-
return 0
74-
75-
# Check if n is missing
76-
if nums[-1] != n:
77-
return n
78-
79-
# Check for missing number in between
80-
for i in range(1, n):
81-
if nums[i] != nums[i - 1] + 1:
82-
return nums[i - 1] + 1
832

84-
return -1 # Should never reach here
3+
# Time: O(?)
4+
# Space: O(?)
5+
def missing_number(self, nums: list[int]) -> int:
6+
# TODO: Implement missing_number
7+
return 0

0 commit comments

Comments
 (0)