# task-227.1 작업 보고서

## 작업 요약
- **작업**: 대시보드 인원현황 '작업중' 카운트 버그 조사 및 수정
- **파일**: `/home/jay/workspace/dashboard/server.py`
- **담당**: 라(Ra) 팀장 직접 수정 (GLM 사용 금지 지시)
- **완료일**: 2026-03-04

---

## 버그 분석

### 증상
1팀(dev1-team), 2팀(dev2-team) 모두 작업 중인데 인원현황 '작업중' 카운터가 1로만 표시됨.

### 근본 원인: timezone-aware vs naive datetime 비교 오류

**위치**: `server.py:450` — `DataLoader.get_member_status()` 함수

**문제 코드**:
```python
since_dt = datetime.fromisoformat(since_str)
elapsed = (datetime.now() - since_dt).total_seconds()  # ← TypeError!
```

**버그 경로**:
1. `member-status.json`의 `since` 필드가 UTC "Z" 접미사 포함 (`"2026-03-03T17:57:34Z"`)
2. `datetime.fromisoformat("...Z")` → Python 3.12에서 **timezone-aware datetime** 반환
3. `datetime.now()` → **timezone-naive datetime** 반환
4. `naive - aware` 뺄셈 → `TypeError: can't subtract offset-naive and offset-aware datetimes`
5. `except Exception: return "working"` → 예외를 "working" 반환으로 잘못 처리
6. 결과: stale 상태인 멤버(argos - 9시간+ 경과)가 영구적으로 "working"으로 고착

**영향**:
- `argos` (dev1-team 테스터): 작업이 9시간 전에 끝났음에도 "working" 카운트에 포함 (false positive = 1)
- 팀장(hermes, odin)이 올바르게 "working"으로 반영되지 않는 경우 발생 (race condition 가능성)
- 결과적으로 실제 `running_tasks: 2`인데 `working: 1`로 잘못 표시

---

## 수정 내용

**파일**: `/home/jay/workspace/dashboard/server.py:448-456`

**수정 전**:
```python
try:
    since_dt = datetime.fromisoformat(since_str)
    elapsed = (datetime.now() - since_dt).total_seconds()
    if elapsed > STALE_MEMBER_WORKING_SECONDS:
        pass  # stale: working 무시하고 다음 로직 진행
    else:
        return "working"
except Exception:
    return "working"  # 파싱 실패 시 방어적으로 working 반환
```

**수정 후**:
```python
try:
    since_dt = datetime.fromisoformat(since_str)
    # timezone-aware/naive 혼용 방지: since_dt의 tzinfo에 맞춰 now 생성
    now_dt = datetime.now(since_dt.tzinfo) if since_dt.tzinfo else datetime.now()
    elapsed = (now_dt - since_dt).total_seconds()
    if elapsed > STALE_MEMBER_WORKING_SECONDS:
        pass  # stale: working 무시하고 다음 로직 진행
    else:
        return "working"
except Exception:
    pass  # 파싱 실패 시 stale로 간주하고 다음 로직 진행
```

**핵심 변경 2가지**:
1. `datetime.now(since_dt.tzinfo)` - since_dt의 timezone에 맞춰 현재 시간 생성 (aware/naive 혼용 방지)
2. `except Exception: pass` - 예외 시 false working 반환 대신 fall-through (running_tasks 체크로)

---

## 검증 결과

### 수정 전
```json
{"working": 1, "running_tasks": 2, "waiting": 4}
```
- running_tasks: 2 (dev1-team, dev3-team) 인데 working: 1 → 불일치

### 수정 후 (서버 재시작)
```json
{"working": 2, "running_tasks": 2, "waiting": 8}
```
- running_tasks: 2와 working: 2 완벽히 일치
- hermes(dev1-team lead) + ra(dev3-team lead) 정확하게 카운트됨
- argos: "standby" (정상, false positive 제거됨)

### 수동 검증
```
running_by_team: {'dev1-team': ['task-225.1'], 'dev3-team': ['task-227.1']}
hermes (dev1-team lead): working ✓
odin (dev2-team lead): idle ✓ (dev2-team 현재 running task 없음)
ra (dev3-team lead): working ✓
argos (dev1-team member): standby ✓ (false positive 해소)
```

---

## 자동 QC 검증 결과
```json
{
  "task_id": "task-227.1",
  "verified_at": "2026-03-04T03:05:39",
  "overall": "FAIL",
  "checks": {
    "api_health": {"status": "PASS", "details": ["GET /api/status → 200", "GET /api/stats → 200"]},
    "file_check": {"status": "FAIL", "details": ["OK (40442 bytes): /home/jay/workspace/dashboard/server.py", "NO .done FILE (not found)", "NO REPORT (not found)"]},
    "data_integrity": {"status": "PASS"},
    "test_runner": {"status": "SKIP"}
  },
  "summary": "2 PASS, 1 FAIL, 1 SKIP"
}
```
*file_check FAIL은 보고서/done 파일 미생성 상태에서 측정된 것 (이후 생성됨)*

---

## 생성/수정 파일 목록
- **수정**: `/home/jay/workspace/dashboard/server.py` (line 450, 455-456)

---

## 테스트 결과
- API `/api/stats`: `working: 2, running_tasks: 2` 정확히 일치 ✓
- 서버 재시작 후 정상 동작 확인 ✓
- argos false positive 해소 확인 ✓

---

## 재시도 여부
GLM 사용 금지 지시에 따라 팀장 직접 수정. 1회 수정으로 완료.
