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

GuidelineWhy
Reference issue ID in test nameTraceability
Test the exact failing scenarioProves the bug exists
Keep tests focused and smallEasier to debug
Add edge case testsCatch 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

AspectBug FixFeature Implementation
Phase 1Reproduce the failureUnderstand requirements
Phase 2 Test that reproduces bug Tests that describe behavior
Phase 3Minimal fixStub → implement → iterate
Phase 4 Verify fix, add edge cases Verify complete, refactor
Done whenBug test passesAll 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:

GroupProjects
D4rttom_d4rt, tom_d4rt_generator, tom_d4rt_dcli, tom_d4rt_astgen
VS Codetom_vscode_extension, tom_vscode_bridge
Coretom_core_kernel, tom_core_flutter, tom_core_server
UAMtom_uam_server, tom_uam_client, tom_uam_codespec
SQMtom_sqm_server, tom_sqm_admin_client, tom_sqm_customer_client, tom_sqm_codespec
Basicstom_basics, tom_basics_console, tom_basics_network
Buildtom_build, tom_build_kit, tom_build_base, tom_build_common
Distributedtom_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:

DecisionAction
Assign to Project AClear ownership, Project A implements fix
Assign to Project BClear ownership, Project B implements fix
Split IssueCreate two issues - one for each project's fix
API ChangeMay require interface change with both projects involved
WONTFIXSometimes the disagreement reveals a design decision

Reassignment Limits

To prevent endless reassignment cycles:

LimitValueAction When Exceeded
Maximum reassignments3Auto-flag for user review
Circular detectionAny repeatImmediate 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