# dispatch.py task-counter 정합성 보장 개선

## 근본 원인
`generate_task_id()` 함수에서 `.task-counter` 파일을 읽어 다음 ID를 결정하지만,
`--task-id`로 직접 지정한 경우 카운터 파일을 업데이트하지 않는다.

결과: `--task-id task-1274.1`로 직접 지정하여 위임하면, `.task-counter`는 여전히 1253에 머물러 있고,
이후 자동 생성 시 1253부터 다시 시작 → **기존 task ID와 충돌**, 보고서/타이머 덮어쓰기 발생.

## 수정 대상
- `/home/jay/workspace/dispatch.py`
- 함수: `generate_task_id()` (341행 부근)
- 함수: `dispatch_task()` (863행 부근, `--task-id` 직접 지정 경로)

## 수정 내용

### Fix 1: generate_task_id()에 카운터 vs timers 정합성 체크 추가
카운터 파일에서 읽은 값이 task-timers.json의 최대값보다 작거나 같으면,
timers.json 최대값 + 1로 보정한다.

```python
# 기존: 카운터 파일 값을 그대로 사용
next_num = int(counter_file.read_text().strip())

# 개선: 카운터 값과 timers.json 최대값 비교, 큰 쪽 사용
counter_num = int(counter_file.read_text().strip())
timers_num = _compute_next_id_from_timers(timer_file)
if counter_num < timers_num:
    logger.warning("카운터(%d) < timers 최대(%d), 보정합니다", counter_num, timers_num)
    next_num = timers_num
else:
    next_num = counter_num
```

### Fix 2: --task-id 직접 지정 시 카운터 파일 업데이트
`dispatch_task()`에서 `task_id`가 외부에서 지정된 경우에도,
해당 번호가 현재 카운터보다 크면 카운터를 업데이트한다.

```python
# dispatch_task() 내, task_id가 외부 지정된 경우 (line 863 부근 아래)
if task_id is not None:
    # 카운터 파일 동기화
    _sync_counter_if_needed(task_id)
```

새 함수 추가:
```python
def _sync_counter_if_needed(task_id: str) -> None:
    """외부 지정된 task_id가 카운터보다 크면 카운터를 업데이트한다."""
    counter_file = WORKSPACE / "memory" / ".task-counter"
    lock_file_path = WORKSPACE / "memory" / ".task-timers.lock"

    parts = task_id.replace("task-", "").split(".")
    try:
        given_num = int(parts[0])
    except (ValueError, IndexError):
        return

    lock_file = open(lock_file_path, "w")
    try:
        fcntl.flock(lock_file, fcntl.LOCK_EX)
        current = 0
        if counter_file.exists():
            try:
                current = int(counter_file.read_text().strip())
            except (ValueError, OSError):
                current = 0
        if given_num >= current:
            counter_file.write_text(str(given_num + 1))
            logger.info("카운터 동기화: %d → %d (외부 task_id=%s)", current, given_num + 1, task_id)
    finally:
        fcntl.flock(lock_file, fcntl.LOCK_UN)
        lock_file.close()
```

### Fix 3: _dispatch_composite()에도 동일한 동기화 적용
`_dispatch_composite()` 함수(616행)에서도 외부 task_id 지정 시 동일하게 `_sync_counter_if_needed()` 호출.

## 테스트 방법
1. 현재 카운터 값 확인: `cat memory/.task-counter` → 1275
2. `--task-id task-1280.1`로 위임 테스트
3. 카운터가 1281로 업데이트되었는지 확인
4. 이후 자동 생성이 1281부터 시작하는지 확인

## 주의사항
- 파일 락(fcntl.LOCK_EX) 반드시 사용 — 동시 dispatch 시 race condition 방지
- 기존 `_compute_next_id_from_timers()` 함수는 수정하지 말 것 (이상치 필터링 로직 유지)
- 로깅 추가 필수: 보정이 발생하면 WARNING 레벨로 기록