Issue Implementation
A test-driven workflow for implementing bugs, fixes, and small features. The core principle: every issue becomes a failing test first.
---
Process Summary
┌─────────────────────────────────────────────────────────────────────────────┐
│ ISSUE IMPLEMENTATION WORKFLOW │
└─────────────────────────────────────────────────────────────────────────────┘
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ 1. Triage │ │ 2. Test │ │ 3. Implement │
│ & Investigate│ ──► │ First │ ──► │ │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
▼ ▼ ▼
Understand the Write failing Code until
problem fully test(s) tests pass
│
▼
┌───────────────┐ ┌───────────────┐
│ 5. Close │ │ 4. Verify │
│ Issue │ ◄── │ & Refactor │
└───────────────┘ └───────────────┘
Key Principle: An issue is complete when all its tests pass. Tests prove the issue is resolved.
---
Phase 1: Triage & Investigate
For Bugs
1. Read the issue description - Understand the reported behavior 2. Reproduce the bug - Confirm you can trigger the failure 3. Identify root cause - Find where the code fails 4. Check related code - Understand the impact scope
For Features
1. Read the issue description - Understand the required behavior 2. Identify affected components - Where will code change? 3. Break into testable slices - What are the discrete behaviors? 4. Check existing tests - What already covers this area?
Investigation Questions
- What is the expected behavior?
- What is the actual (current) behavior?
- What inputs trigger the issue?
- What code paths are involved?
- Are there related issues or tests?
---
Phase 2: Test First
This is the most important phase. The issue transforms into one or more failing tests.
For Bugs
test('should not throw when input is empty [BUG-123]', () {
// This test reproduces the reported bug
// It MUST fail before the fix and pass after
final processor = DataProcessor();
// This currently throws, but shouldn't
expect(() => processor.process(''), returnsNormally);
});
For Features
test('should return filtered items matching criteria [FEAT-456]', () {
// This test describes the new behavior
// It fails because the feature doesn't exist yet
final filter = ItemFilter();
final items = [Item('a'), Item('ab'), Item('abc')];
final result = filter.byMinLength(items, 2);
expect(result, hasLength(2));
expect(result.map((i) => i.name), containsAll(['ab', 'abc']));
});
Test Writing Guidelines
| Guideline | Why |
|---|---|
| Reference issue ID in test name | Traceability |
| Test the exact failing scenario | Proves the bug exists |
| Keep tests focused and small | Easier to debug |
| Add edge case tests | Catch related issues |
Minimum: One Test Per Issue
- Every issue MUST have at least one associated test
- Complex issues may expand into multiple tests during implementation
- Tests become permanent guards against regression
---
Phase 3: Implement
For Bugs
1. Fix minimally - Change only what's needed to pass the test 2. Run the failing test - Confirm it now passes 3. Run all related tests - Check for regressions 4. Add edge case tests - If fix revealed other gaps
Bug Fix Cycle:
─────────────
1. Run failing test → RED
2. Make minimal fix
3. Run test → GREEN
4. Run all tests → GREEN
5. Add edge case tests if needed
6. Refactor if needed (keep GREEN)
For Features
1. Stub the interface - Create classes/methods that compile but throw 2. Run tests - Confirm they fail for the right reason 3. Implement incrementally - One slice at a time 4. Add more tests - As implementation reveals new cases
Feature Implementation Cycle:
─────────────────────────────
1. Write test for first slice → RED
2. Stub interface (throws NotImplementedError)
3. Implement until test passes → GREEN
4. Write test for next slice → RED
5. Implement until test passes → GREEN
6. Repeat until feature complete
7. Refactor (keep GREEN)
Implementation Rules
- Never skip the failing test step - The test must fail first
- Commit when green - Don't leave failing tests uncommitted
- One behavior at a time - Small increments are easier to debug
---
Phase 4: Verify & Refactor
Verification Checklist
- [ ] Original failing test now passes
- [ ] All existing tests still pass
- [ ] No analyzer warnings or errors (
dart analyze) - [ ] Code is formatted (
dart format) - [ ] Edge cases are covered
Refactoring
Once tests are green, improve code quality:
1. Apply clean code principles 2. Run tests after each change 3. Keep tests green throughout
Quality Check Commands
dart analyze # No errors or warnings
dart format . # Code is formatted
dart test # All tests pass
testkit :test # With baseline tracking
---
Phase 5: Close Issue
An issue is ready to close when:
1. All tests pass - Both new and existing 2. Code review complete (if applicable) 3. No analyzer issues 4. Changes committed and pushed
Commit Message Format
Fix: Brief description [ISSUE-123]
- Added test to reproduce the bug
- Fixed by handling empty input case
- Added edge case tests for null and whitespace
---
Bug vs Feature Summary
| Aspect | Bug Fix | Feature Implementation |
|---|---|---|
| Phase 1 | Reproduce the failure | Understand requirements |
| Phase 2 | Test that reproduces bug | Tests that describe behavior |
| Phase 3 | Minimal fix | Stub → implement → iterate |
| Phase 4 | Verify fix, add edge cases | Verify complete, refactor |
| Done when | Bug test passes | All feature tests pass |
---
Cross-Project Issues
During implementation, you may discover issues in other projects. How to handle depends on whether the projects are in the same project group.
Project Groups
A project group is a set of tightly coupled projects that share a common domain and are typically developed together:
| Group | Projects |
|---|---|
| D4rt | tom_d4rt, tom_d4rt_generator, tom_d4rt_dcli, tom_d4rt_astgen |
| VS Code | tom_vscode_extension, tom_vscode_bridge |
| Core | tom_core_kernel, tom_core_flutter, tom_core_server |
| UAM | tom_uam_server, tom_uam_client, tom_uam_codespec |
| SQM | tom_sqm_server, tom_sqm_admin_client, tom_sqm_customer_client, tom_sqm_codespec |
| Basics | tom_basics, tom_basics_console, tom_basics_network |
| Build | tom_build, tom_build_kit, tom_build_base, tom_build_common |
| Distributed | tom_dist_ledger, tom_distributed_orchestration, tom_process_monitor |
Same Group: Fix Immediately
If the issue is in a project within the same group, treat it as part of the current implementation:
Current issue in tom_d4rt
│
▼
Found bug in tom_d4rt_generator (same group)
│
▼
┌─────────────────────────────────────────┐
│ FIX IMMEDIATELY │
│ │
│ 1. Write failing test in tom_d4rt_gen │
│ 2. Fix the bug │
│ 3. Run tests → GREEN │
│ 4. Continue with original issue │
└─────────────────────────────────────────┘
Rationale: Same-group projects are so interdependent that blocking issues must be resolved to continue.
Different Group: File Issue and Wait
If the issue is in a project outside your group, do NOT fix it directly:
Current issue in tom_uam_server
│
▼
Found bug in tom_basics (different group)
│
▼
┌─────────────────────────────────────────┐
│ FILE ISSUE AND WAIT │
│ │
│ 1. File new issue for tom_basics │
│ 2. Mark current issue as "Blocked by" │
│ 3. Current issue → WAITING │
│ 4. New issue → TRIAGE │
│ 5. Assign & implement new issue │
│ 6. When resolved → Resume original │
└─────────────────────────────────────────┘
Rationale: Different groups may have different priorities, test suites, and ownership. Changes need proper triage.
Cross-Project Issue Workflow
┌─────────────────────────────────────────────────────────────────────────────┐
│ CROSS-PROJECT ISSUE WORKFLOW │
└─────────────────────────────────────────────────────────────────────────────┘
Discover issue in other project
│
▼
┌───────────────────┐
│ Same project │
│ group? │
└───────────────────┘
│ │
YES NO
│ │
▼ ▼
┌──────────┐ ┌──────────────────┐
│ Fix now │ │ File new issue │
│ inline │ │ with description │
└──────────┘ └──────────────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────────────┐
│ Continue │ │ Mark current as │
│ original │ │ "Blocked by" │
└──────────┘ └──────────────────┘
│
▼
┌──────────────────┐
│ Current → WAIT │
│ New → TRIAGE │
└──────────────────┘
│
▼
┌──────────────────┐
│ New issue goes │
│ through normal │
│ implementation │
└──────────────────┘
│
▼
┌──────────────────┐
│ Resume original │
│ when unblocked │
└──────────────────┘
Filing a Blocking Issue
When creating an issue for another project group:
1. Title: Clear description of the problem 2. Description: - What you were doing when you found it - Steps to reproduce - Expected vs actual behavior - Link to the blocked issue 3.
Labels: blocking, affected project(s)
Example:
## tom_basics: StringExtension.truncate throws on empty string
### Context
Found while implementing tom_uam_server issue #45.
### Steps to Reproduce
''.truncate(10); // Throws RangeError
### Expected
Return empty string (or original string if shorter than limit).
### Actual
Throws `RangeError: Invalid value: Not in range 0..0, inclusive: 10`
### Blocks
- tom_uam_server#45
Issue Reassignment
During investigation, you may discover the issue actually belongs to a different project than originally assigned. When this happens:
Issue #42 assigned to tom_d4rt
│
▼
Investigation reveals bug is in tom_basics
│
▼
┌─────────────────────────────────────────┐
│ REASSIGNMENT │
│ │
│ 1. Document findings in issue comment │
│ 2. Use issuekit :assign to reassign │
│ 3. Add "Reassigned from: tom_d4rt" │
│ 4. Remove previous test entry if any │
│ 5. Issue goes back to TRIAGE │
└─────────────────────────────────────────┘
Reassignment command:
# Issue was originally assigned to tom_d4rt, now reassigning to tom_basics
issuekit :assign 42 --project tom_basics --module strings --note "Reassigned from: tom_d4rt. Root cause is in StringExtension, not parser."
Circular Reassignment Detection
A circular reassignment occurs when an issue gets reassigned back to a project that already investigated and reassigned it. This indicates disagreement about ownership.
┌─────────────────────────────────────────────────────────────────────────────┐
│ CIRCULAR REASSIGNMENT │
└─────────────────────────────────────────────────────────────────────────────┘
Issue #42 assigned to tom_d4rt
│
▼
tom_d4rt investigates → "This is a tom_basics bug"
│
▼
Reassigned to tom_basics
│
▼
tom_basics investigates → "No, this is tom_d4rt misusing our API"
│
▼
Reassigned back to tom_d4rt ← CIRCULAR DETECTED
Detection Rule: If an issue is reassigned to a project that appears anywhere in the issue's reassignment history, it is circular.
Resolution Process:
1. STOP - Do not continue reassigning 2. Flag for User Review - Add
needs-triage and circular labels 3. Document Both Perspectives
- Comment summarizing each project's position 4. Escalate - User must decide ownership
Flagging circular issue:
issuekit :edit 42 --add-label needs-triage --add-label circular --note "Circular reassignment detected. tom_d4rt says tom_basics issue. tom_basics says API misuse by tom_d4rt. Requires user decision."
User Resolution Options:
| Decision | Action |
|---|---|
| Assign to Project A | Clear ownership, Project A implements fix |
| Assign to Project B | Clear ownership, Project B implements fix |
| Split Issue | Create two issues - one for each project's fix |
| API Change | May require interface change with both projects involved |
| WONTFIX | Sometimes the disagreement reveals a design decision |
Reassignment Limits
To prevent endless reassignment cycles:
| Limit | Value | Action When Exceeded |
|---|---|---|
| Maximum reassignments | 3 | Auto-flag for user review |
| Circular detection | Any repeat | Immediate flag for user review |
Tracking reassignments:
Each reassignment adds a comment like:
**Reassignment #2** (of 3 max)
- From: tom_d4rt
- To: tom_basics
- Reason: Root cause is in StringExtension.truncate
- Previous assignments: tom_d4rt (initial)
---
Example: Bug Fix
Issue: "Parser crashes on empty input"
// Phase 2: Write failing test
test('should return empty result for empty input [BUG-789]', () {
final parser = Parser();
expect(() => parser.parse(''), returnsNormally); // Currently throws
expect(parser.parse(''), isEmpty);
});
// Phase 3: Implement fix
class Parser {
List<Token> parse(String input) {
if (input.isEmpty) return []; // Added this line
// ... rest of implementation
}
}
// Phase 4: Add edge case tests
test('should handle whitespace-only input [BUG-789]', () {
final parser = Parser();
expect(parser.parse(' '), isEmpty);
});
---
Example: Small Feature
Issue: "Add case-insensitive search option"
// Phase 2: Write failing tests
test('should find matches case-insensitively when flag set [FEAT-101]', () {
final searcher = Searcher();
final result = searcher.search('HELLO', items, caseInsensitive: true);
expect(result, contains(Item('hello')));
});
// Phase 3a: Stub
class Searcher {
List<Item> search(String query, List<Item> items, {bool caseInsensitive = false}) {
throw UnimplementedError(); // Stub - test will fail
}
}
// Phase 3b: Implement
class Searcher {
List<Item> search(String query, List<Item> items, {bool caseInsensitive = false}) {
final normalizedQuery = caseInsensitive ? query.toLowerCase() : query;
return items.where((item) {
final normalizedName = caseInsensitive ? item.name.toLowerCase() : item.name;
return normalizedName.contains(normalizedQuery);
}).toList();
}
}
// Phase 4: Add more tests
test('should preserve case sensitivity by default [FEAT-101]', () {
final searcher = Searcher();
final result = searcher.search('HELLO', items);
expect(result, isNot(contains(Item('hello'))));
});
---
Appendix: Analyzer False Positives
Some analyzer warnings are expected and should not block issue completion.
Unused Imports for Reflection
Imports required for *.reflection.dart generation may appear unused:
// ignore: unused_import
import 'package:tom_core/reflection_types.dart'; // Required for reflection
Path Dependency Warnings
Expected during development with local packages:
dependencies:
tom_core:
path: ../tom_core # Warning is expected
Stale Cache Errors
If errors reference non-existent files:
rm -rf .dart_tool && dart pub get
# Then reload VS Code window
---
See Also
- Test-Driven Development - TDD principles and cycle
- Unit Tests - Dart-specific test patterns
- Clean Code Principles - Refactoring guidance
- Specification Workflow - For larger features