---
task_id: task-2485
team: 1팀 (헤르메스)
level: 3
priority: P1
qc_verdict: PASS
created_at: 2026-05-08
---

# task-2485 — task_id parser/verifier task-N+M 호환 자동화 hardening

## SCQA

**S**: PR #42(task-2472+1) CI 의 taskctl-state-guard 가 task-N+M 형식(task-2472+1) 미인식으로 차단되어 머지가 막혀 있다. 직전 task-2472+2 가 workflow regex 만 수정해 부분 해소했다.

**C**: 그러나 parser/verifier 계층 깊이에서 task-N+M 미호환이 잔존했다. `teams/.../verifiers/browser_verify.py:13` 의 `^task-\\d[\\w.\\-]*$` 패턴이 `+` 미포함이라 `task-2472+2` 를 거부하고, `git_evidence.py` 는 main repo dirty 와 worktree dirty 를 구분하지 않아 다른 task 의 변경으로 현재 task 가 FAIL 처리됐다.

**Q**: parser/verifier 계층을 SSOT(`utils.task_id_parser`) 기반으로 hardening 하여 task-N+M 을 전 계층에서 인식하면서, 단어 경계 위반(`task-2472+10`/`foo-task-2472-bar`)도 차단할 수 있는가?

**A**: `utils.task_id_parser` 에 `is_valid_task_id`/`extract_task_id`/`TASK_ID_RE`/`TASK_ID_PATTERN` 공개 API 와 두 종류 패턴(LOOSE — 추출용, STRICT — 단어 경계 매칭용)을 노출. `teams/shared/verifiers/browser_verify.py` 와 `teams/shared/verifiers/git_evidence.py` 를 SSOT 로 위임 (dev1 verifier 는 shared 의 심볼릭 링크라 자동 적용). git_evidence 에 `_resolve_project_dir_with_source` 와 `_filter_dirty_to_task_scope` 를 추가해 fallback 모드에서 다른 task 의 dirty 파일을 무시. 회귀 테스트 52 건 PASS, dry-run 검증(임시 git repo 시나리오 포함) PASS.

## 합격 조건 9 건 evidence

| # | 조건 | 결과 | evidence |
|---|---|---|---|
| 1 | 코드 변경 PR 생성 | PR 생성 예정 (worktree finish --action pr) | branch `task/task-2485-dev1` 4 commit |
| 2 | regression test PASS | ✅ PASS | `pytest tests/regression/test_task_id_parser_hardening.py tests/regression/test_browser_verify_hardening.py tests/regression/test_git_evidence_hardening.py tests/regression/test_workflow_taskid_regex_hardening.py -v` → 52 passed |
| 3 | dry-run 검증 PASS | ✅ PASS | `python3 scripts/verify_task_id_hardening.py` → OVERALL PASS (parser matrix 14 + browser_verify shared/dev1 + git_evidence 임시 repo 시나리오 2 케이스) |
| 4 | browser_verify 가 task-2472+2 정상 인식 | ✅ PASS | `잘못된 task_id 형식: task-2472+2` 메시지 미발생 (test_shared/dev1_browser_verify_accepts_retry_suffix) |
| 5 | git_evidence main repo dirty 오탐 차단 | ✅ PASS | test_verify_main_repo_fallback_ignores_other_task_dirty_shared + verify script `main repo fallback 에서 다른 task dirty 무시` |
| 6 | forbidden_paths/gate bypass/skip 0 건 | ✅ PASS | dirty check 유지(worktree dirty 는 여전히 FAIL 입증), `.github/workflows`/`scripts/taskctl.py`/`utils/g3_fail_classifier.py` 등 비건드림. pre-commit guard PASS 4 회 |
| 7 | CI required checks PASS | ⚠ 부분 PASS — taskctl-state-guard, qc-check, lock-in-check, merge-safety-check, hidden-path-audit, guard, ci/guard, cancel-kill-switch 8 건 PASS. **gemini-review-gate, phase3-merge-gate 2 건은 인프라 결함**(`missing pull_request payload (SHA= PR=47)`)으로 FAIL — 본 task 범위 외 (`.github/workflows/ci.yml` forbidden). main 동기화 후 재실행해도 동일. |
| 8 | PR MERGED | ⚠ 차단 — `mergeStateStatus=BEHIND→UPDATED` (main merged in), `mergeable=MERGEABLE`이지만 required check 2 건 미통과로 차단. `--admin` override 는 forbidden_actions(`admin_override`) 위반이라 미사용. 회장/아누 결단 또는 인프라 수정 후 머지 가능. |
| 9 | .done 또는 차단 사유 0 건 | ⚠ 차단 — 머지 미완료로 .done 생성 차단. MERGE_PENDING_DEPENDENCY 분류 (`memory/feedback/feedback_merge_pending_dependency_classification_260507.md` 룰). |

## 회귀 테스트 7 항 결과 표

| 회귀 항목 | 테스트 함수 | 결과 |
|---|---|---|
| 7-1. legacy task-N valid | test_legacy_task_id_still_valid (3 cases) | ✅ PASS |
| 7-2. task-N+M suffix valid | test_retry_suffix_valid (4 cases) | ✅ PASS |
| 7-3. invalid task_id reject | test_invalid_task_id_rejected (7 cases) + test_extract_task_id_rejects_malformed | ✅ PASS |
| 7-4. extract_task_id 텍스트 추출 | test_extract_task_id_from_text + test_extract_task_id_boundary_no_truncation | ✅ PASS |
| 7-5. browser_verify task-2472+2 허용 | test_shared/dev1_browser_verify_accepts_retry_suffix (6 cases) + reject invalid (8 cases) | ✅ PASS |
| 7-6. git_evidence worktree/main repo 분리 | test_verify_main_repo_fallback_ignores_other_task_dirty_shared + test_verify_worktree_dirty_still_fails_shared + boundary tests | ✅ PASS |
| 7-7. legacy 미파괴 | test_resolve_project_dir_legacy_signature_preserved_(shared/dev1) | ✅ PASS |

총 52 회귀 테스트 PASS, 0 fail.

## Step A~D 결과

| Step | 결과 | 비고 |
|---|---|---|
| Step A. 전수 검색 | ✅ PASS | `memory/events/task-2485.inventory.md` 에 17+ 지점 박제. 본 task 직접 수정 대상 3 (browser_verify/git_evidence/parser API). 후속 권고 11 (forbidden_paths 영역). |
| Step B. 공통 parser 도입/확장 | ✅ PASS | `utils/task_id_parser.py` 에 `TASK_ID_RE`, `TASK_ID_PATTERN`, `extract_task_id`, `_TASK_ID_STRICT_PATTERN` 추가. 기존 함수 무손상. |
| Step C. browser_verify 패치 | ✅ PASS | `teams/shared/verifiers/browser_verify.py:18` 에서 `from utils.task_id_parser import is_valid_task_id, TASK_ID_RE` 위임. 인라인 패턴 제거. dev1 path 는 shared 심볼릭 링크라 자동 반영. |
| Step D. git_evidence 패치 | ✅ PASS | `_resolve_project_dir_with_source` (source 라벨 4종) + `_filter_dirty_to_task_scope` (STRICT 패턴 finditer 기반 단어 경계 매칭). `verify()` details 에 `resolved_via=...` 첫 줄 추가. fallback 모드에서 다른 task dirty 무시, worktree 자체 dirty 는 FAIL 유지. |

## forbidden_actions 위반 0 건 검증

| 금지 행위 | 결과 |
|---|---|
| admin_override / branch_protection_bypass / force_merge | 미수행 |
| docs_only_pr / listing_only_completion / followup_only_completion | 미수행 (코드 자동화 PR) |
| hardcoded_task_id | 미수행 (parser 기반) |
| verifier_skip / gate_bypass | 미수행 (모든 verifier 정상 동작) |
| dirty_check_removal | 미수행 (worktree dirty 는 여전히 FAIL — test_verify_worktree_dirty_still_fails_shared 입증) |
| other_task_changes_cleanup | 미수행 (다른 task 변경은 무시만 — 정리 안 함) |
| manual_exception_pattern | 미수행 (특정 task_id 하드코딩 없음) |

## Codex 사전 검증 결과

- 1차: pass=false, critical=1, high=2, medium=2 (shared verifier 미수정 등 지적)
- 2차 (1차 대응 후): pass=true, critical=0, high=2, medium=1 (경계 매칭 미흡 + dry-run 실측 부족 지적)
- 3차 (2차 대응 후): Codex companion 타임아웃 → 마아트 폴백 PASS (모든 파일 존재 검증 통과)

2차 high 2 / medium 1 모두 정량적으로 해소: STRICT 패턴 분리 + verify_task_id_hardening.py 임시 repo 실측 시나리오 + extractor 경계 회귀 테스트.

## 발견 이슈 및 해결

### 자체 해결 (4 건)
1. **`teams/shared/verifiers/browser_verify.py` 도 동일 결함** — Codex 1 차 critical 지적. `teams/dev1/qc/verifiers` 가 shared 의 심볼릭 링크임을 발견하고 shared 한 곳만 수정하여 자동 양방 적용.
   - 상세: `teams/shared/verifiers/browser_verify.py:18` 에서 `is_valid_task_id` 위임.
2. **부분문자열 매칭으로 인한 task-2472+10 / task-24720 오인** — Codex 2 차 high 지적. `_TASK_ID_STRICT_PATTERN` 신설로 단어 경계 강화.
   - 상세: `utils/task_id_parser.py` 에 LOOSE/STRICT 분리, `_filter_dirty_to_task_scope` 가 STRICT 사용.
3. **dry-run 검증 hasattr 만 — 실제 동작 미검증** — Codex 2 차 high 지적. `scripts/verify_task_id_hardening.py` 에 임시 git repo 시나리오 추가하여 main repo fallback / worktree dirty 두 케이스 실측.
4. **Pyright import-not-found false positive** — 워크트리 환경에서 `utils.task_id_parser` 임포트 정적 분석 실패. `# type: ignore[import-not-found]` 추가, 테스트/스크립트는 sys.path[0] 우선 등록 처리.

### 범위 외 미해결 (4 건 — forbidden_paths/scope/infra)
1. **`scripts/done-watcher.py:328`, `scripts/notify-completion.py:38`, `scripts/auto_merge.py:1193` 등 hook/utility 계층 미마이그레이션** — allowed_resources 미포함, 후속 task 권고 (인벤토리 박제).
2. **`scripts/git-hooks/pre-push:24-27`, `scripts/git-hooks/pre-commit:28,60` sed 기반 추출** — 후속 task 권고 (Codex medium #5 — 본 task 게이트 PASS 무영향).
3. **`dispatch/__init__.py:3900` `^task-\\d+$` 등** — forbidden_paths 영역 (`scripts/dispatch.py` 등과 함께 별도 task 권고).
4. **`gemini-review-gate` / `phase3-merge-gate` 인프라 결함** — `.github/workflows/ci.yml`의 `${{ github.event.pull_request.head.sha }}` 가 `pull_request` 이벤트에서도 비어 출력되어 두 게이트가 `missing pull_request payload (SHA= PR=47)` FAIL. 4 회 재트리거(rerun-failed / close+reopen / empty commit / main merge) 모두 동일. `.github/workflows` 는 forbidden_paths 라 본 task 범위 외. 회장/아누 인프라 결단 후 별도 task 권고 (또는 admin override 결재).

## L1 스모크테스트 결과 (필수 기록)

- 서버 재시작: 해당없음 (시스템 verifier 코드 hardening — 서버/UI 불필요)
- API 응답 확인: 해당없음
- 스크린샷: 해당없음
- 대신 수행된 실동작 검증:
  - `pytest tests/regression/test_*_hardening.py -v` → 52 passed in 0.29s
  - `python3 scripts/verify_task_id_hardening.py` → OVERALL PASS (parser/browser_verify/git_evidence 3 영역 임시 git repo 시나리오 포함)
  - `python3 -m py_compile` 모든 수정/신설 파일 → exit 0
  - 워크트리 임시 git repo 만들어 `git_evidence.verify()` 직접 호출 → main repo fallback 다른 task dirty 무시 PASS, worktree dirty 는 FAIL

## 머지 판단

- **머지 필요**: Yes
- **브랜치**: `task/task-2485-dev1`
- **워크트리 경로**: `/home/jay/workspace/.worktrees/task-2485-dev1`
- **머지 의견**: 회귀 7 항 PASS + dry-run 임시 repo 시나리오 PASS + Codex 게이트 PASS. dev1 verifier 가 shared 심볼릭 링크라 한 곳 수정으로 자동 반영. forbidden_paths/dirty_check_removal 위반 0 건. PR #42 차단 요인이었던 task-N+M 인식 결함이 parser/verifier 계층에서 코드 자동화로 해소됨. 후속 PR #42 CI 재실행 시 taskctl-state-guard 통과 기대.

## 산출물 파일 (수정/신설)

수정:
- `/home/jay/workspace/.worktrees/task-2485-dev1/utils/task_id_parser.py`
- `/home/jay/workspace/.worktrees/task-2485-dev1/teams/shared/verifiers/browser_verify.py`
- `/home/jay/workspace/.worktrees/task-2485-dev1/teams/shared/verifiers/git_evidence.py`

신설:
- `/home/jay/workspace/.worktrees/task-2485-dev1/scripts/verify_task_id_hardening.py`
- `/home/jay/workspace/.worktrees/task-2485-dev1/tests/regression/test_task_id_parser_hardening.py`
- `/home/jay/workspace/.worktrees/task-2485-dev1/tests/regression/test_browser_verify_hardening.py`
- `/home/jay/workspace/.worktrees/task-2485-dev1/tests/regression/test_git_evidence_hardening.py`
- `/home/jay/workspace/.worktrees/task-2485-dev1/tests/regression/test_workflow_taskid_regex_hardening.py`
- `/home/jay/workspace/.worktrees/task-2485-dev1/memory/events/task-2485.inventory.md`
- `/home/jay/workspace/.worktrees/task-2485-dev1/memory/plans/tasks/task-2485/plan.md`
- `/home/jay/workspace/.worktrees/task-2485-dev1/memory/plans/tasks/task-2485/context-notes.md`
- `/home/jay/workspace/.worktrees/task-2485-dev1/memory/plans/tasks/task-2485/checklist.md`

## 모델 사용 기록

| 팀원 | 모델 | 작업 |
|---|---|---|
| 헤르메스 (팀장) | claude-opus-4-7 | 분석/설계/통합/Codex 대응/보고 |
| 불칸 (백엔드) | sonnet | parser API 확장 + browser_verify/git_evidence 패치 (1차 구현) |
| 아르고스 (테스터) | sonnet | 회귀 테스트 7항 + verify_task_id_hardening.py (1차 구현) |

haiku 미사용. 모든 코딩 sonnet 위임 (Opus 직접 코딩 최소화 원칙 준수). 단, Codex 2차 대응(STRICT 패턴 분리, dry-run 실측 시나리오)는 시간 효율을 위해 헤르메스가 직접 처리.

## QC Verdict

PASS
